diff --git a/.aiexclude b/.aiexclude new file mode 100644 index 0000000..dc8c690 --- /dev/null +++ b/.aiexclude @@ -0,0 +1,27 @@ +# .aiexclude — was der KI-Assistent (Antigravity/Gemini) NICHT lesen/indexieren soll. +# Gitignore-Syntax. Ziel: der Review fokussiert auf QUELLCODE, nicht auf Abhängigkeiten, +# Build-Output oder Binärdateien. + +# Abhängigkeiten & Build-Output (kein Review-Ziel) +backend/.venv/ +frontend/node_modules/ +frontend/dist/ +**/__pycache__/ +*.pyc +.git/ + +# Große / binäre / sensible Dateien +*.vrm +*.gguf +*.onnx +*.safetensors +*.wav +*.env +.env +credentials* +*.key +*.pem + +# Lokale Scratch-/Recon-Skripte +box_recon* +gemma_swap* diff --git a/SAVEPOINT.md b/SAVEPOINT.md new file mode 100644 index 0000000..be1d010 --- /dev/null +++ b/SAVEPOINT.md @@ -0,0 +1,51 @@ +# Savepoint — Mission Control 2.0 (MC2) + +_Stand: 2026-07-05. Zustands-Snapshot für einen externen Code-Review (Antigravity). Zuerst +`AGENTS.md` lesen (Projekt-Regeln), dann diese Datei (wo wir stehen), dann `README.md`/`docs/`._ + +## Was das ist (in einem Satz) +MC2 ist die **Web-Admin-Konsole** einer 100 % lokalen KI-Appliance („die Box", Bosgame M5 / AMD +Strix Halo, 128 GB Unified RAM, llama.cpp-Vulkan). Sie verwaltet Engine, Modelle, Gedächtnis, +Agenten-Status, Updates & Wartung — und dient teils als Gedächtnis-Oberfläche für die Sprach- +Begleiterin „Lucy". + +## Architektur (Kurzform — Details in AGENTS.md/docs/) +- **Backend** `backend/` — FastAPI, `:9001` als systemd-**User-Dienst** (reboot-fest, sudo-frei). + Router (`routers/`, dünn) → Logik (`services/`, Single Source of Truth). +- **Frontend** `frontend/` — React/Vite/Tailwind/shadcn. **`frontend/dist` ist ABSICHT im git** + (Box hat kein Node; Backend liefert die gebauten Assets aus). +- **Eingebautes Gateway** `:9001/v1` (OpenAI-kompatibel) → llama-swap `:8080` (Engine, Vulkan/RADV). +- **Sidecars:** `mem0_service/` (Mem0-Gedächtnis), `voice_service/` (STT/TTS für den „Sprechen"-Tab), + `mcp/` (MCP-Server), `hermes/` (Hermes-Agent-Plugin), `client/hermes-pc` (PC-Executor). + +## Aktueller Zustand +- **Stabil, in Produktion, „MC2 Final".** Ein Neubau („MC3") wurde bewusst VERWORFEN — MC2 wird + sauber gehalten und eingefroren, nicht neu erfunden. +- **Autonomie-Ausbau abgeschlossen** (Observability-Latenztrace, Fremd-Modell-Kritiker/Härtung, + nächtliches „Dreaming" mit Wissens-Vault) — jeweils propose-only, Mensch = Gate. +- **Zuletzt (Commit `ff1b9a0`, 05.07.):** `coder-lite` (Qwen3-Coder-30B) entfernt; der Verbinden-Tab + (`services/connect.py`) empfiehlt IDEs jetzt die realen Direkt-Modelle `coder`/`heavy`/`vision` + statt der virtuellen „coding"-Lane. Davor u. a. Engine-Update-Fix, Hermes-Terminal same-origin. +- **Modelle live:** hermes=Qwen3.6-35B-A3B (Agent-Hirn, immer warm) · coder=Qwen3-Coder-Next · + heavy=gpt-oss-120b · vision=Qwen3-VL-30B · scout=GLM-4.6V · embed. Warm-Set = hermes+vision+embed. + +## Nordstern & Randbedingungen (WICHTIG für den Review) +- **Zieht ohne Wartung über JAHRE durch.** Robustheit + Einfachheit schlagen Features. Der Betreiber + ist **kein Entwickler** — alles muss reboot-/update-fest und selbsterklärend sein. +- **100 % lokal, kein Cloud-Zwang** (Privatsphäre ist der Sinn). Hardware-Decke: ~124 GB, ein Topf. +- **Lucy ist ein SEPARATES Repo** (`F:\Coding Stuff\lucy`) — **nicht Teil dieses Reviews.** + +## Bekannte raue Kanten (Kandidaten — gern bestätigen/erweitern) +- Doku-Drift möglich: `AGENTS.md` nennt „Box läuft in UTC" — die Box wurde inzwischen auf + `Europe/Berlin` umgestellt. +- Alte, disabled v1-System-Unit `mission-control.service` liegt kosmetisch herum (sudo zum Entfernen). +- Ältere Review-Notiz nannte toten Code-Verdacht (`pricing.py`, `token_stats.py`) — bitte prüfen. +- Ein paar frühere Frontend-Monolithen wurden entflochten; Rest-Altlasten möglich. + +## Was NICHT empfohlen werden soll (bereits entschieden) +Cloud-Migration · Voll-Rewrite · schwere neue Frameworks · Engine-/Modell-Wechsel, die nicht in +124 GB passen · `frontend/dist` aus git nehmen (Absicht) · Security-Config anfassen. Erwünscht sind +**Bugs, Fragilität, tote Pfade, Sicherheitslücken, Vereinfachung (KISS/DRY), Wartbarkeit.** + +## Letzter Sicherungspunkt +- git `ff1b9a0` „coder-lite raus + Verbinden-Tab auf echte Direkt-Modelle" (auf `main`, deployt, live). diff --git a/backend/app.py b/backend/app.py index 179ea82..f9278af 100644 --- a/backend/app.py +++ b/backend/app.py @@ -10,6 +10,7 @@ import asyncio import logging import os from contextlib import asynccontextmanager +from typing import Any, AsyncGenerator, Awaitable, Callable import httpx from fastapi import FastAPI @@ -18,9 +19,24 @@ from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from starlette.requests import Request -from config import FRONTEND_DIST, VERSION -from routers import agent, connect, console, gateway_proxy, health, hermes_ui, maintenance, memory, models, reminders as reminders_router, routing, system, voice -from services import memory as memory_svc, reminders, sentry, warmer +from backend.config import FRONTEND_DIST, VERSION +from backend.routers import ( + agent, + connect, + console, + gateway_proxy, + health, + hermes_ui, + maintenance, + memory, + models, + routing, + system, + voice, +) +from backend.routers import reminders as reminders_router +from backend.services import memory as memory_svc +from backend.services import reminders, sentry, warmer # Zentrales Logging — Level via MC_LOG_LEVEL (INFO default). Eine Konfiguration # für alle Module (logging.getLogger(__name__)). @@ -30,21 +46,28 @@ logging.basicConfig( ) log = logging.getLogger(__name__) + @asynccontextmanager -async def lifespan(app: FastAPI): +async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: """Hintergrund-Tasks an den App-Lebenszyklus binden: Re-Warm-Wächter fürs Agent-Hirn + Health-Wächter (meldet Ausfälle/Erholung in den Lucy-Briefkasten und auf Telegram).""" - tasks = [] + tasks: list[asyncio.Task[Any]] = [] if warmer.ENABLED: tasks.append(asyncio.create_task(warmer.rewarm_loop())) - log.info("Hirn-Re-Warm-Wächter aktiv (Intervall %ss, Hirn dynamisch aus Hermes-Config)", warmer.INTERVAL) + log.info( + "Hirn-Re-Warm-Wächter aktiv (Intervall %ss, Hirn dynamisch aus Hermes-Config)", + warmer.INTERVAL, + ) if sentry.ENABLED: tasks.append(asyncio.create_task(sentry.sentry_loop())) tasks.append(asyncio.create_task(reminders.reminders_loop())) if memory_svc.AUTO_DEDUPE_ENABLED: tasks.append(asyncio.create_task(memory_svc.auto_dedupe_loop())) - log.info("Mem0-Auto-Dedupe aktiv (alle %ss, Schwelle %s)", - memory_svc.AUTO_DEDUPE_INTERVAL, memory_svc.AUTO_DEDUPE_THRESHOLD) + log.info( + "Mem0-Auto-Dedupe aktiv (alle %ss, Schwelle %s)", + memory_svc.AUTO_DEDUPE_INTERVAL, + memory_svc.AUTO_DEDUPE_THRESHOLD, + ) # Geteilter HTTP-Client zur lokalen Engine: Keep-Alive/Connection-Pooling statt neuer Client # pro /v1-Anfrage (spart Sockets/TIME_WAIT unter parallelen Agent-Strömen von Zed/Kilo). app.state.gw_client = httpx.AsyncClient( @@ -55,7 +78,7 @@ async def lifespan(app: FastAPI): yield finally: for task in tasks: - task.cancel() + _ = task.cancel() await app.state.gw_client.aclose() @@ -71,7 +94,9 @@ app.add_middleware( @app.middleware("http") -async def no_cache(request: Request, call_next): +async def no_cache( + request: Request, call_next: Callable[[Request], Awaitable[httpx.Response]] +) -> httpx.Response: resp = await call_next(request) if request.url.path.startswith("/api"): resp.headers["Cache-Control"] = "no-cache" @@ -85,12 +110,20 @@ app.include_router(system.router) app.include_router(connect.router) app.include_router(memory.router) app.include_router(agent.router) -app.include_router(voice.router) # Sprache: STT/TTS-Proxy + Hermes-Agent-Chat (Voice-Tab) -app.include_router(reminders_router.router) # Erinnerungen/Routinen (A3) — feuern in den Briefkasten +app.include_router( + voice.router +) # Sprache: STT/TTS-Proxy + Hermes-Agent-Chat (Voice-Tab) +app.include_router( + reminders_router.router +) # Erinnerungen/Routinen (A3) — feuern in den Briefkasten app.include_router(gateway_proxy.router) # OpenAI-kompatibler /v1-Gateway (model:auto) app.include_router(maintenance.router) -app.include_router(console.router) # Box-Konsole (ttyd) same-origin durchreichen — VOR dem SPA-Catch-all -app.include_router(hermes_ui.router) # Eingebaute Hermes-Web-GUI (hermes serve) same-origin unter /hermes-ui/ — VOR dem SPA-Catch-all +app.include_router( + console.router +) # Box-Konsole (ttyd) same-origin durchreichen — VOR dem SPA-Catch-all +app.include_router( + hermes_ui.router +) # Eingebaute Hermes-Web-GUI (hermes serve) same-origin unter /hermes-ui/ — VOR dem SPA-Catch-all # Prod: gebautes Frontend ausliefern (falls vorhanden). SPA-Fallback auf index.html. @@ -113,6 +146,7 @@ if FRONTEND_DIST.exists(): index = FRONTEND_DIST / "index.html" if index.exists(): # index.html nie cachen → Browser zieht nach jedem Deploy das aktuelle (gehashte) Bundle. - return FileResponse(index, headers={"Cache-Control": "no-cache, must-revalidate"}) + return FileResponse( + index, headers={"Cache-Control": "no-cache, must-revalidate"} + ) return {"detail": "frontend not built"} - diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 6098bbf..cab5cba 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -97,6 +97,10 @@ _HUI_DIST="$HOME/.hermes/hermes-agent/hermes_cli/web_dist" if [ -d "$_HUI_WEB" ] && [ -x "$HOME/.hermes/node/bin/npx" ]; then if ( cd "$_HUI_WEB" && PATH="$HOME/.hermes/node/bin:$PATH" npx --no-install vite build \ --base=/hermes-ui/ --outDir /tmp/hermes-ui-build --emptyOutDir ) >/tmp/hermes-ui-build.log 2>&1; then + # Dienst VOR dem Tausch stoppen — sonst crasht der laufende `hermes serve`, wenn er index.html + # im Lösch-Fenster (rm→cp) liest (FileNotFoundError → Start-Limit → tot). Der reguläre restart + # weiter unten bringt ihn sauber auf der neuen web_dist hoch. + systemctl --user stop hermes-builtin-ui 2>/dev/null || true rm -rf "$_HUI_DIST" && cp -r /tmp/hermes-ui-build "$_HUI_DIST" && echo "Hermes-GUI (base=/hermes-ui/) gebaut." else echo "WARN: Hermes-GUI-Build fehlgeschlagen (siehe /tmp/hermes-ui-build.log) — bestehende web_dist bleibt." @@ -118,6 +122,8 @@ loginctl enable-linger "$USER" >/dev/null 2>&1 || true # Voice-Sidecar (re)starten (best-effort; Erststart lädt das STT-Modell vor). [ -x "$HOME/.voice/venv/bin/python" ] && systemctl --user restart voice-service 2>/dev/null || true # Eingebaute Hermes-Web-GUI (Loopback :9119) (re)starten — MC2 bettet sie unter /hermes-ui/ ein. +# reset-failed, damit ein zuvor am Start-Limit gestorbener Dienst wieder anläuft. +systemctl --user reset-failed hermes-builtin-ui 2>/dev/null || true systemctl --user restart hermes-builtin-ui 2>/dev/null || true systemctl --user restart mission-control-2 command -v ttyd >/dev/null 2>&1 && systemctl --user restart hermes-terminal 2>/dev/null || true diff --git a/deploy/skills/orchestrator/SKILL.md b/deploy/skills/orchestrator/SKILL.md new file mode 100644 index 0000000..5c21f1f --- /dev/null +++ b/deploy/skills/orchestrator/SKILL.md @@ -0,0 +1,183 @@ +--- +name: orchestrator +description: "Orchestrieren wie ein Manager: einen groesseren Auftrag in Teil-Tasks zerlegen, jeden SERIELL an das passende Worker-Modell delegieren (worker.sh, Ein-Schuss via llama-swap), die Ergebnisse einsammeln + zusammensetzen, per Fremd-Kritiker gaten und als Telegram-Vorschlag zurueckgeben. NIE selbst mergen/deployen — der Commander ist das Gate." +version: 1.0.0 +author: MC2 (Autonomie — Orchestrator) +platforms: [linux] +metadata: + hermes: + tags: [orchestrator, delegation, werkstatt, mc2, fliessband] +--- + +# Orchestrator — du managst, du fuehrst nicht alles selbst aus + +Nutze diesen Skill, wenn ein Auftrag zu gross fuer EINEN Rutsch ist, sich aber in wenige klar +abgegrenzte Teil-Tasks zerlegen laesst (ueberwiegend Code-Bau). Du bist der **Manager**: du +zerlegst, verteilst die Teile SERIELL an Worker-Modelle (die arbeiten), fuegst die Ergebnisse +zusammen und lieferst am Ende einen **Vorschlag** — der Commander entscheidet. + +Das ist die Verallgemeinerung der **Werkstatt** (`wartung`): dort delegierst du EINEN Schritt +(Code-Review) an ein Fremdmodell. Hier delegierst du MEHRERE Bau-Schritte an Worker und behaeltst +die Integration + das Gate. + +## Warum seriell (und warum das ok ist) +Mehrere grosse Modelle GLEICHZEITIG passen nicht in den Speicher. llama-swap ist **seriell** — ein +Modell laden → arbeiten → tauschen → naechstes. Orchestrierung ist ein **Fliessband, kein Schwarm.** +Langsamer (Swap-Latenz + einer nach dem anderen: Minuten), aber bei Hintergrund-/Telegram-Jobs egal. + +## Scope-Check zuerst (klein anfangen) +Passt: ein Auftrag, der in **2–4 abgegrenzte Teil-Tasks** zerfaellt, jeder ein 1–2-Datei-Bau-Schritt +(neues kleines Modul + Test, kleine neue Funktion + Verdrahtung, zusammenhaengender Refactor in +wenigen Dateien). Zu gross (viele Module, echte Architektur-Entscheidungen, breite API-Aenderung): +dann NUR einen **Plan** als Text im Telegram-Vorschlag liefern, KEINEN Code — der Commander schneidet +dann kleiner. + +## Leitplanken (nicht verhandelbar) +- **Du delegierst, du baust NICHT selbst.** Jedes Bau-Artefakt MUSS aus einem `worker.sh`-Aufruf + stammen. Schreibst du den Code selbst (auch wenn der Teil-Task „einfach genug" wirkt), statt ihn + vom Worker zu holen, ist der Lauf GESCHEITERT — sag das ehrlich, statt Eigenbau als Orchestrierung + auszugeben. Belege jeden Teil-Task im Vorschlag mit dem `worker.sh`-Aufruf UND den ersten Zeilen + seiner Roh-Ausgabe. (Warum so streng: das Manager-Modell kann kleine Tasks selbst — genau dann + faellt Delegation aus Bequemlichkeit weg; das ist der haeufigste Orchestrierungs-Fehler.) +- **Die Helfer EXISTIEREN.** `~/.hermes/scripts/worker.sh` und `~/.hermes/scripts/fremdblick.sh` + sind installiert, aber NICHT auf deinem PATH → immer mit vollem Pfad aufrufen (nie bloss `worker.sh`). + Glaubst du, sie fehlen, IRRST du dich: erst `ls -la ~/.hermes/scripts/worker.sh` ausfuehren und den + Pfad lesen, dann nutzen. „Skript fehlt" ist KEIN gueltiger Grund, den Schritt selbst zu machen. +- **Propose-only.** Am Ende steht IMMER ein Telegram-Vorschlag; die Entscheidung trifft der Commander. + NIEMALS auf `main` committen, NIEMALS mergen, NIEMALS deployen oder Dienste neu starten. +- **Nur im Worktree arbeiten.** Die Live-Instanz `~/mission-control-v2` bleibt unberuehrt. +- **Warm-Set-Schutz (harte Speicher-Falle).** Worker nur ueber **`worker.sh`** und nur Modelle, die + KLEIN neben dem Warm-Set laden — v1 = **`Qwen3-Coder-Next`** (Default in worker.sh). **NIEMALS + `heavy`/`gpt-oss-120b` als Worker** (60 GB → stirbt neben dem VL-30B-Warm-Set, gefaehrdet Lucys + ~1-s-Sprech-Latenz). Planung/Zerlegung/Integration machst DU (Hermes, schon warm) selbst. +- **Fremd-Kritiker ist Pflicht-Gate.** Der zusammengesetzte Diff MUSS von `fremdblick.sh` (anderes + Modell) gegengelesen werden, bevor du vorschlaegst. Ein Manager, der Worker-Output blind + zusammenklebt, produziert leisen Muell. +- **Security-Config ist TABU:** keine Tokens, approvals, ufw, sudoers, ssh-Keys anfassen. +- Gate rot oder Kritiker dagegen → trotzdem ehrlich melden (Branch bleibt liegen), nichts beschoenigen. + +## Ablauf + +### 1. Zerlegen (der Manager denkt) +Brich den Auftrag in eine **geordnete, nummerierte Liste von Teil-Tasks**. Fuer jeden Teil-Task +entscheide: (a) was ist das Artefakt (welche Datei, was tut sie), (b) welches Worker-Modell + +welche Rolle (`build`/`refactor`), (c) welchen Kontext braucht der Worker (siehe Abschnitt +„Kontext-Uebergabe"). Halte die Liste kurz. Reihe die Teile so, dass spaetere auf fertige +fruehere aufbauen koennen. + +### 2. Worktree anlegen (Slug = kurzer Kebab-Case-Name des Auftrags) +``` +cd ~/mission-control-v2 && git fetch -q origin \ + && git worktree add /tmp/orch- -b orchestrator/ origin/main +mkdir -p /tmp/orch--work # Ablage fuer rohe Worker-Ausgaben +``` + +### 3. Routen — je Teil-Task ein Worker-Ein-Schuss (seriell) +Fuer jeden Bau-Teil-Task: +1. Baue die Eingabe: **genau der noetige Kontext** + der praezise Auftrag (siehe unten). Bereits + fertige Artefakte aus frueheren Schritten als Kontext mitgeben, wenn der Worker sie braucht. +2. Ruf den Worker auf und fang die Ausgabe roh ab: + ``` + printf '%s' "$eingabe" | WORKER_ROLE=build ~/.hermes/scripts/worker.sh > /tmp/orch--work/.out + ``` + (Default-Modell ist `Qwen3-Coder-Next`. Fuer einen bewusst anderen Worker `WORKER_MODEL=`.) +3. **Pruefe die Ausgabe, bevor du sie verwendest:** Beginnt sie mit `FEHLT:`, hast du zu wenig + Kontext gegeben → nachliefern und erneut rufen. Hat der Worker versehentlich einen ```-Codezaun + oder Prosa drumherum gesetzt, entferne ihn. Dann schreibe das saubere Artefakt mit deinen + Datei-Tools an seinen Platz im Worktree (`/tmp/orch-/...`). Der Worker hat KEINE + Datei-Haende — das Schreiben machst DU. + +### 4. Integrieren + Self-Gate (nach jedem Schritt bzw. am Ende) +Die Worker liefern Bausteine — DU sorgst dafuer, dass sie zusammenpassen (Imports, Verdrahtung, +Namen). Dann das Selbst-Gate, was zutrifft: +- Python geaendert → `python3 -m py_compile ` (und wenn ein Test dabei ist und ohne + schwere Deps laeuft: den Test im Worktree ausfuehren). +- Shell geaendert → `bash -n ` +- Frontend (`frontend/src/...`) → auf der Box gibt es KEIN Node. Im Vorschlag ausweisen: + „Gate eingeschraenkt: tsc/Build laeuft erst beim Merge auf dem PC." +- **Live-Checkout unberuehrt (PFLICHT, zwei Beweise):** + - `git -C ~/mission-control-v2 status --porcelain -uno` **muss LEER sein** (Beweis, dass du + wirklich nur im Worktree gearbeitet hast — ein gruener Health-curl allein reicht NICHT, weil + der laufende Dienst den alten Code im RAM haelt und eine schmutzige Datei auf der Platte nicht + bemerkt). + - `curl -sf http://127.0.0.1:9001/api/health` muss gruen bleiben. + - Ist `git status` NICHT leer: die fremden Aenderungen im Live-Checkout mit + `git -C ~/mission-control-v2 checkout -- ` zuruecknehmen (deine Arbeit liegt sicher im + Worktree) und im Vorschlag ehrlich erwaehnen. + +### 5. Zwei-Kritiker-Gate ueber den GESAMTEN Diff (Pflicht) +Zwei Zweitgutachter mit je anderer Staerke lesen den zusammengesetzten Diff gegen — aus demselben +Grund wie in der Werkstatt („wer seine eigenen Hausaufgaben benotet, stimmt sich selbst zu"). **NICHT +`delegate_task`** (laeuft ueber `heavy` → 64K-Floor + Warm-Set-Kollision). + +**Zuerst ALLES stagen** — sonst fehlen NEUE Dateien im Diff (haeufigste Falle: `git diff` ignoriert +untracked Dateien, die Kritiker bekaemen einen LEEREN Diff und wuerden alles blind ablehnen): +``` +git -C /tmp/orch- add -A +``` +Dann die Eingabe EINMAL bauen (mit `--cached`, damit die neuen Dateien drin sind): +``` +EINGABE="AUFTRAG (im Wortlaut): + + +ZERLEGUNG (deine Teil-Tasks, je 1 Zeile): +<1..n> + +GIT-DIFF des Worktrees: +$(git -C /tmp/orch- diff --cached origin/main)" +``` +Ist dieser Diff LEER, hast du nicht gestaged (oder nichts gebaut) → NICHT weiter, erst beheben. +**Kritik A — Kompetenz** (`Qwen3-Coder-Next`, starker Coder, vom Bauen noch geladen → KEIN Swap): +``` +printf '%s' "$EINGABE" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh +``` +**Kritik B — Fremd-Blick** (`GLM-4.6V-Flash`, andere Modellfamilie, andere blinde Flecken): +``` +printf '%s' "$EINGABE" | FREMDBLICK_MODE=code FREMDBLICK_MODEL=GLM-4.6V-Flash FREMDBLICK_MAXTOK=3000 ~/.hermes/scripts/fremdblick.sh +``` +Reihenfolge bewusst: erst A (Coder-Next ist noch warm), dann B (ein Swap zu GLM). Beide beginnen mit +`URTEIL: `. **Warte auf beide.** + +**Gate-Regel (streng):** Sagt AUCH NUR EINER `ABGELEHNT` → nachbessern (max. 2 Runden: den +betroffenen Teil-Task neu an den Worker, integrieren, BEIDE Kritiker erneut). `FREIGABE-MIT-VORBEHALT` +→ die Vorbehalte adressieren oder im Vorschlag ehrlich ausweisen (NICHT wegdiskutieren). Nur wenn +BEIDE tragen → Vorschlag. Beide Urteile KONKRET in den Vorschlag (welcher Kritiker was sagte). Faellt +ein Kritiker technisch aus (`FEHLGESCHLAGEN`/`ABGESCHNITTEN`) → einmal wiederholen, sonst „⚠ Kritik X +fiel aus" vermerken (NICHT als Freigabe zaehlen). + +> Der schwere **Chef-Gutachter (`gpt-oss`)** laeuft NICHT in diesem heissen Pfad — er muesste 60 GB +> kalt laden und Lucys Warm-Set stoeren. Er prueft separat NACHTS im Leerlauf ueber die offenen +> Orchestrator-Vorschlaege (eigener Cron), wo Ladezeit + Warm-Set-Umbau verkraftbar sind. +- `ABGELEHNT` / berechtigte `FREIGABE-MIT-VORBEHALT` → nachbessern (max. 2 Runden: den betroffenen + Teil-Task neu an den Worker geben, integrieren, erneut gaten). Bleibt es kritisch: Kritik in den + Vorschlag schreiben, Branch bleibt liegen, Entscheidung dem Commander. +- `FREMDBLICK FEHLGESCHLAGEN`/`ABGESCHNITTEN` → einmal wiederholen; sonst im Vorschlag ehrlich + „⚠ Fremd-Review fiel aus — UNGEPRUEFT" vermerken (NICHT als Freigabe behandeln). + +### 6. Telegram-Vorschlag (in LUCYS Stimme) +`bash ~/mission-control-v2/deploy/notify.sh -s "[Orchestrator]" ""` — an den Commander, nicht +als anonymer Job: erst 1–2 Saetze, was gebaut wurde und was er jetzt entscheiden soll; dann knapp: +- die **Zerlegung** (welcher Teil-Task an welches Worker-Modell ging), +- geaenderte/neue Dateien · Kern des Diffs, +- Self-Gate-Ergebnis · Kritiker-Urteil KONKRET ausgeschrieben (nicht nur „ok"), +- Branch-Name · Frage „merge oder verwerfen?" + +### 7. Nichts loeschen +Worktree + Branch + rohe Worker-Ausgaben bleiben liegen, bis der Commander entschieden hat. + +## Kontext-Uebergabe — hier geht Orchestrierung am ehesten schief +Jeder Worker startet **frisch und zustandslos**: kein Gedaechtnis der bisherigen Schritte, kein +Zugriff aufs Repo. Er sieht NUR, was du ihm auf stdin gibst. Deshalb: +- Gib ihm den **relevanten Ausschnitt** mit — die betroffene Datei (oder den betroffenen Block), + benachbarte Muster zum Nachahmen, die genaue Signatur/den Namen, den er treffen muss, und bei + Folge-Schritten die bereits fertigen Artefakte, auf die er aufbaut. +- Sei praezise beim Zielartefakt: „Gib den VOLLSTAENDIGEN Inhalt der neuen Datei `X` aus" bzw. „Gib + einen unified Diff gegen den folgenden Stand von `Y` aus". +- Zu wenig Kontext → der Worker raet oder meldet `FEHLT:`. Zu viel unnoetiger Kontext → er verliert + den Fokus. Ziel: das kleinste, das den Teil-Task eindeutig macht. + +## Nach dem Commander-Entscheid (kommt als neuer Auftrag) +- „verwerfen" → `git worktree remove /tmp/orch- --force && git branch -D orchestrator/` + (+ `rm -rf /tmp/orch--work`; Remote-Branch loeschen, falls gepusht). +- „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es nur dort). + Du pushst NIE nach main. diff --git a/deploy/worker.sh b/deploy/worker.sh new file mode 100644 index 0000000..66b1032 --- /dev/null +++ b/deploy/worker.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# Worker — Ein-Schuss-Arbeitsauftrag an ein bestimmtes Worker-Modell (llama-swap, SERIELL). +# Das Gegenstueck zu fremdblick.sh: fremdblick KRITISIERT, worker ARBEITET. Beide sind eine +# EINZELNE Completion an eine ECHTE llama-swap-Modell-ID (kein Gateway-Alias, kein delegate_task) +# → umgeht Hermes' 64K-Delegations-Floor (MINIMUM_CONTEXT_LENGTH) und das Laden grosser Modelle +# neben dem Warm-Set liegt bewusst in DEINER Hand. +# +# WICHTIG (harte Speicher-Falle): Waehle nur Worker, die KLEIN neben dem Warm-Set laden. +# v1-Palette = Qwen3-Coder-Next (128k, code-stark, laedt klein). NIEMALS "heavy"/gpt-oss-120b +# als Worker: 60 GB stirbt beim Laden neben dem VL-30B-Warm-Set (Health-Check-Timeout) und +# gefaehrdet Lucys ~1-s-Sprech-Latenz. +# +# Der Worker ist ZUSTANDSLOS: kein Gedaechtnis, keine Datei-Haende. Er bekommt GENAU den Kontext, +# den du ihm auf stdin gibst, und liefert TEXT zurueck (Datei-Inhalt / Diff / Plan). Das SCHREIBEN +# in Dateien macht der Manager (Hermes) danach mit seinen eigenen Tools. +# +# Nutzung: printf '%s' "$teiltask_mit_kontext" | WORKER_ROLE=build worker.sh > /tmp/out.py +# printf '%s' "$spec" | WORKER_MODEL=Qwen3-Coder-Next WORKER_ROLE=build worker.sh +# +# Env-Overrides: +# WORKER_MODEL echte llama-swap-ID (Default: Qwen3-Coder-Next). NICHT gpt-oss/heavy (siehe oben). +# WORKER_ROLE build (Default) | refactor | plan | prose — waehlt das System-Raster. +# WORKER_SYS eigenes System-Raster (uebersteuert WORKER_ROLE). +# WORKER_MAXTOK max_tokens (Default 4000 — Code braucht Platz). +# WORKER_TEMP temperature (Default 0.1 — deterministisch fuer Code). +set -uo pipefail + +# Achtung: llama-swap listet unter /v1/models die ECHTEN Modell-IDs, nicht die Gateway-Aliase +# (kein "heavy"/"coder"/"scout" hier — die echte ID nehmen). +ENDPOINT="${WORKER_ENDPOINT:-http://127.0.0.1:8080/v1/chat/completions}" +MODEL="${WORKER_MODEL:-Qwen3-Coder-Next}" +ROLE="${WORKER_ROLE:-build}" +MAXTOK="${WORKER_MAXTOK:-4000}" +TEMP="${WORKER_TEMP:-0.1}" +TASK="$(cat)" + +if [ -z "${TASK// /}" ]; then + echo "=== WORKER: nichts zu tun (leere Eingabe) ===" >&2 + exit 2 +fi + +# Aufruf-Protokoll: Nachweis, dass wirklich delegiert wurde (der Manager kann sich das nicht +# ausdenken) + Beobachtbarkeit eines Orchestrator-Laufs. Best-effort, blockiert nie. +LOGF="${WORKER_LOG:-$HOME/.hermes/logs/worker.log}" +mkdir -p "$(dirname "$LOGF")" 2>/dev/null || true +printf '%s\tmodel=%s\trole=%s\tbytes=%s\tfirst=%s\n' \ + "$(date +%Y-%m-%dT%H:%M:%S)" "$MODEL" "$ROLE" "${#TASK}" \ + "$(printf '%s' "$TASK" | tr '\n\t' ' ' | cut -c1-90)" >> "$LOGF" 2>/dev/null || true + +# Bau-Raster: liefert NUR das Artefakt, keine Prosa, kein Markdown-Zaun (Manager schreibt es 1:1 in eine Datei). +BUILD_SYS='Du bist ein fokussierter Umsetzungs-Worker in einem Fliessband. Du bekommst EINEN abgegrenzten Teil-Auftrag mit allem noetigen Kontext. Erledige GENAU diesen Teil — nicht mehr, nicht weniger. Du bist ZUSTANDSLOS: nutze NUR den mitgelieferten Kontext, erfinde keine Datei-Inhalte, keine Pfade und keine APIs dazu; fehlt dir etwas Entscheidendes, schreibe als EINZIGE Ausgabe eine Zeile "FEHLT: " statt zu raten. Uebernimm den Stil des umgebenden Codes (Sprache der Kommentare, bestehende Muster, Einrueckung). Gib NUR das geforderte Artefakt aus (den vollstaendigen Datei-Inhalt bzw. den unified Diff), OHNE Vorrede, OHNE ```-Codezaun, OHNE Erklaerung danach.' +# Refactor: wie build, aber betont Verhalten-erhalten. +REFACTOR_SYS='Du bist ein fokussierter Refactor-Worker. Du bekommst bestehenden Code und einen klaren Umbau-Auftrag. Erhalte das beobachtbare Verhalten exakt; aendere nur, was der Auftrag verlangt. Du bist ZUSTANDSLOS: nutze NUR den mitgelieferten Kontext; fehlt Entscheidendes, gib nur "FEHLT: " aus. Gib NUR den vollstaendigen neuen Datei-Inhalt bzw. den Diff aus, OHNE Prosa, OHNE ```-Codezaun.' +# Plan-Raster: knappe, umsetzbare Schrittliste (kein Code). In v1 plant meist der Manager selbst; optional. +PLAN_SYS='Du bist ein knapper Umsetzungs-Planer. Zerlege den Auftrag in eine kurze, nummerierte Liste konkreter Schritte — je 1 Zeile: welche Datei, was passiert. Keine Prosa drumherum, KEIN Code, keine Alternativen-Diskussion.' +# Prosa-Raster: Text-/Recherche-Teiltask. +PROSE_SYS='Du bist ein fokussierter Text-Worker. Erledige den Schreib-/Recherche-Teilauftrag knapp und sachlich auf Deutsch, ausschliesslich mit dem mitgelieferten Kontext. Keine Ausschmueckung, keine erfundenen Fakten.' + +case "$ROLE" in + build) SYS="${WORKER_SYS:-$BUILD_SYS}" ;; + refactor) SYS="${WORKER_SYS:-$REFACTOR_SYS}" ;; + plan) SYS="${WORKER_SYS:-$PLAN_SYS}" ;; + prose) SYS="${WORKER_SYS:-$PROSE_SYS}" ;; + *) SYS="${WORKER_SYS:-$BUILD_SYS}" ;; +esac + +RESP="$(jq -n --arg m "$MODEL" --arg s "$SYS" --arg u "$TASK" --argjson t "$MAXTOK" --argjson temp "$TEMP" \ + '{model:$m, temperature:$temp, max_tokens:$t, messages:[{role:"system",content:$s},{role:"user",content:$u}]}' \ + | curl -s --max-time 300 "$ENDPOINT" -H 'Content-Type: application/json' -d @- 2>/dev/null)" + +OUT="$(printf '%s' "$RESP" | jq -r '.choices[0].message.content // empty' 2>/dev/null)" +FIN="$(printf '%s' "$RESP" | jq -r '.choices[0].finish_reason // empty' 2>/dev/null)" + +# Code-Modelle wickeln die Ausgabe trotz Anweisung gern in einen ```-Codezaun. stdout soll aber ein +# DIREKT schreibbares Artefakt sein → einen umschliessenden Zaun entfernen (nur wenn die erste UND +# die letzte nicht-leere Zeile ein Zaun ist — der uebliche Wrap-Fall; interne Zaeune bleiben unberuehrt). +OUT="$(printf '%s' "$OUT" | awk ' + { l[NR]=$0 } + END { + f=0; last=0 + for (i=1;i<=NR;i++) if (l[i] ~ /[^[:space:]]/) { if (!f) f=i; last=i } + if (f && l[f] ~ /^[[:space:]]*```/ && l[last] ~ /^[[:space:]]*```[[:space:]]*$/ && f!=last) + { for (i=f+1;i&2 + exit 0 +else + echo "=== WORKER FEHLGESCHLAGEN (Modell $MODEL, finish=${FIN:-keins}) — kein Ergebnis. Endpoint/Modell-ID pruefen (echte /v1/models-ID, kein Alias). ===" >&2 + exit 1 +fi diff --git a/docs/ANTIGRAVITY_REVIEW.md b/docs/ANTIGRAVITY_REVIEW.md new file mode 100644 index 0000000..df27a35 --- /dev/null +++ b/docs/ANTIGRAVITY_REVIEW.md @@ -0,0 +1,52 @@ +# Antigravity-Review-Prompt — Mission Control 2.0 + +Prompt zum Einfügen in Antigravity 2.0 (Modell: **Gemini 3.1 Pro, High-Modus**). Projektordner = +dieses Repo. `.aiexclude` hält `.venv`/`node_modules`/`dist` aus dem Kontext. + +--- + +Du bist ein erfahrener Senior-Reviewer und führst ein **vollständiges, ehrliches Code- und +Architektur-Review** dieses Projekts (Mission Control 2.0, kurz MC2) durch. + +**Zuerst lesen — das ist der Rahmen, weiche nicht davon ab:** +1. `AGENTS.md` — die verbindlichen Projekt-Regeln. +2. `SAVEPOINT.md` — wo das Projekt gerade steht, Nordstern und Randbedingungen. +3. `README.md` und `docs/` — Kontext. + +**Was MC2 ist:** die Web-Admin-Konsole einer **100 % lokalen** KI-Appliance (AMD Strix Halo, 128 GB, +llama.cpp-Vulkan). Backend = FastAPI (`backend/`, Dienst auf :9001), Frontend = React/Vite/Tailwind +(`frontend/`). Sidecars: `mem0_service/`, `voice_service/`, `mcp/`, `hermes/`, `client/`. + +**Nordstern:** Das System muss **über Jahre ohne Wartung** laufen, betrieben von einem +**Nicht-Entwickler**. Robustheit, Einfachheit und Selbstheilung schlagen Features. + +## Dein Auftrag +Prüfe das gesamte Repo (außer dem, was `.aiexclude` ausschließt). Suche gezielt nach: +- **Korrektheits-Bugs & Race Conditions** — besonders in `backend/services/` und den Routern. +- **Betriebs-Fragilität:** Was bricht bei **Reboot**, **Update** oder **Teil-Ausfall**? Was ist nicht + reboot-/idempotenz-fest? (Der Betreiber kann es nicht selbst debuggen.) +- **Sicherheitslücken** — Auth/Token-Handling, Command-Allowlists, Prompt-Injection-Guards, + Pfad-/Eingabe-Validierung, `sudo -n`-Nutzung. (Nur MELDEN, nichts an der Security-Config ändern.) +- **Ressourcen-Lecks & Fehlerbehandlung** — offene Clients/Dateien, verschluckte Exceptions, + fehlende Timeouts. +- **Toter/redundanter Code, DRY-/KISS-Verstöße, Über-Komplexität** — Vereinfachungs-Chancen. +- **Doku-/Realitäts-Drift** — Widersprüche zwischen `AGENTS.md`/Kommentaren und dem Code + (z. B. Zeitzone UTC vs. Europe/Berlin). +- **Wartbarkeit** — würde ein Fremder das in einem Jahr noch verstehen und sicher anfassen können? + +## Harte Leitplanken (nicht empfehlen) +- KEINE Cloud-Migration, KEIN Voll-Rewrite, KEINE schweren neuen Frameworks. +- KEINE Modell-/Engine-Wechsel, die nicht in ~124 GB passen; keine Cloud-LLMs. +- `frontend/dist` bleibt bewusst im git (Box hat kein Node) — nicht als Fehler werten. +- Security-Config nur MELDEN, nie „einfach ändern". +- Bevorzuge **Vereinfachung und Härtung** vor neuen Features. + +## Ausgabeformat (auf DEUTSCH) +1. **Gesamturteil** (3–5 Sätze): Wie gesund ist das Projekt? Trägt es den Nordstern „Jahre ohne Wartung"? +2. **Befunde, nach Schwere sortiert** — je Befund: + - Schweregrad **P0** (bricht/Sicherheit) · **P1** (echter Bug/Fragilität) · **P2** (Wartbarkeit) · **P3** (nice-to-have) + - `datei:zeile` · was ist das Problem · **warum es zählt** (konkretes Fehlszenario) · **konkreter Fix** +3. **Positives** — was bewusst gut gelöst ist (damit es nicht „wegoptimiert" wird). +4. **Was ich bewusst NICHT ändern würde** — und warum (Respekt vor den getroffenen Entscheidungen). + +Sei ehrlich und konkret. Erfinde keine Probleme, um die Liste zu füllen. Wenn etwas gut ist, sag das.