Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| abc8a365d2 | |||
| 1cd492eb33 | |||
| 3abc133118 | |||
| 0354ce999f |
@@ -5,7 +5,7 @@ from fastapi.responses import JSONResponse, StreamingResponse
|
|||||||
|
|
||||||
from config import LLAMA_SWAP_URL
|
from config import LLAMA_SWAP_URL
|
||||||
from services.gateway_stream import record_stream_chunk, record_usage
|
from services.gateway_stream import record_stream_chunk, record_usage
|
||||||
from services.router_logic import VISION_CAPABLE, choose_for_lane, has_image
|
from services.router_logic import IMAGE_PART_TYPES, VISION_CAPABLE, choose_for_lane, has_image
|
||||||
from services.routing_policy import load_policy
|
from services.routing_policy import load_policy
|
||||||
|
|
||||||
router = APIRouter(prefix="/v1")
|
router = APIRouter(prefix="/v1")
|
||||||
@@ -19,6 +19,61 @@ _LANG_DIRECTIVE = os.environ.get(
|
|||||||
"Quellcode, Bezeichner und Shell-Befehle bleiben unverändert.")
|
"Quellcode, Bezeichner und Shell-Befehle bleiben unverändert.")
|
||||||
|
|
||||||
|
|
||||||
|
# Bild-Beschreibung für Coder-Ziele: Prompt bewusst auf wörtliche Wiedergabe von
|
||||||
|
# Code/Fehlermeldungen getrimmt — der Coder arbeitet nur mit diesem Text weiter.
|
||||||
|
_BILD_BESCHREIB_PROMPT = os.environ.get(
|
||||||
|
"MC_CODER_IMAGE_PROMPT",
|
||||||
|
"Beschreibe dieses Bild vollstaendig und praezise auf Deutsch: sichtbarer Text, "
|
||||||
|
"Code, Zahlen/Daten, UI-Elemente, Layout, Farben und alles Auffaellige. "
|
||||||
|
"Sichtbaren Code und Fehlermeldungen gib WOERTLICH wieder.")
|
||||||
|
|
||||||
|
|
||||||
|
def _ist_coder_alias(alias: str) -> bool:
|
||||||
|
"""Coder-Ziele (coder, rohe Qwen-Coder-IDs) bekommen Bild-BESCHREIBUNGEN statt der
|
||||||
|
stumpfen Vision-Umleitung — die Code-Frage bleibt beim Spezialisten."""
|
||||||
|
return "coder" in (alias or "").lower()
|
||||||
|
|
||||||
|
|
||||||
|
async def _bilder_fuer_coder_beschreiben(body: dict, client, vision_alias: str) -> bool:
|
||||||
|
"""Ersetzt jeden Bild-Part durch eine Text-Beschreibung vom Vision-Modell.
|
||||||
|
|
||||||
|
Idee aus einem verwaisten, nie verdrahteten Patch in der Hermes-Quelle
|
||||||
|
(gateway/platforms/api_server.py, 07/2026) — bei der Projekt-Review 15.07.
|
||||||
|
regelkonform hierher umgezogen (Archiv: docs/archiv/). True = alle Bilder
|
||||||
|
ersetzt; False = eine Analyse scheiterte, Aufrufer nutzt die normale
|
||||||
|
Vision-Umleitung als Fallback.
|
||||||
|
"""
|
||||||
|
fundstellen: list[tuple[dict, int, dict]] = []
|
||||||
|
for m in body.get("messages") or []:
|
||||||
|
if not isinstance(m, dict) or not isinstance(m.get("content"), list):
|
||||||
|
continue
|
||||||
|
for i, part in enumerate(m["content"]):
|
||||||
|
if isinstance(part, dict) and part.get("type") in IMAGE_PART_TYPES:
|
||||||
|
fundstellen.append((m, i, part))
|
||||||
|
for nr, (msg, idx, part) in enumerate(fundstellen, start=1):
|
||||||
|
frage = {
|
||||||
|
"model": vision_alias,
|
||||||
|
"stream": False,
|
||||||
|
"max_tokens": 700,
|
||||||
|
"messages": [{"role": "user",
|
||||||
|
"content": [part, {"type": "text", "text": _BILD_BESCHREIB_PROMPT}]}],
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
# Grosszuegiger Timeout: VL-30B ist on-demand (Kaltladen ~30 s) + Beschreibung ~25 s.
|
||||||
|
r = await client.post(f"{LLAMA_SWAP_URL}/v1/chat/completions", json=frage, timeout=240.0)
|
||||||
|
text = ""
|
||||||
|
if r.status_code == 200:
|
||||||
|
text = ((r.json().get("choices") or [{}])[0].get("message") or {}).get("content") or ""
|
||||||
|
if not text.strip():
|
||||||
|
return False
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
msg["content"][idx] = {"type": "text", "text": (
|
||||||
|
f"[Bild {nr}: dem Request lag ein Bild bei — {vision_alias} beschreibt es so:\n"
|
||||||
|
f"{text.strip()}]")}
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _inject_language(body: dict, alias: str) -> None:
|
def _inject_language(body: dict, alias: str) -> None:
|
||||||
"""Deutsch-Direktive anhängen. An die ERSTE System-Message (viele Chat-Templates
|
"""Deutsch-Direktive anhängen. An die ERSTE System-Message (viele Chat-Templates
|
||||||
erwarten nur eine), sonst als neue System-Message. `hermes` ausgenommen — Lucys
|
erwarten nur eine), sonst als neue System-Message. `hermes` ausgenommen — Lucys
|
||||||
@@ -101,11 +156,19 @@ async def _proxy(path: str, request: Request):
|
|||||||
routed = {"x-mc-routed-to": requested}
|
routed = {"x-mc-routed-to": requested}
|
||||||
|
|
||||||
pol = load_policy()
|
pol = load_policy()
|
||||||
|
client = request.app.state.gw_client # geteilter Keep-Alive-Client (siehe app.py lifespan)
|
||||||
# Bild-Weiche (Faden 11): Requests mit Bild-Anhang automatisch ans Vision-Modell umleiten,
|
# Bild-Weiche (Faden 11): Requests mit Bild-Anhang automatisch ans Vision-Modell umleiten,
|
||||||
# sofern das Ziel nicht ohnehin bildfähig ist. Das MTP-Hirn (fast/hermes) kann keine Bilder —
|
# sofern das Ziel nicht ohnehin bildfähig ist. Das MTP-Hirn (fast/hermes) kann keine Bilder —
|
||||||
# so bleibt Lucy schnell, ohne dass jemand manuell das Modell wechselt (Entscheid Weg A).
|
# so bleibt Lucy schnell, ohne dass jemand manuell das Modell wechselt (Entscheid Weg A).
|
||||||
|
# Coder-Sonderweg (15.07.): Coder-Ziele behalten den Request — die Bilder werden vorab vom
|
||||||
|
# Vision-Modell BESCHRIEBEN und als Text injiziert (Screenshot-Debugging bleibt beim Coder).
|
||||||
vision_alias = pol.get("vision")
|
vision_alias = pol.get("vision")
|
||||||
if vision_alias and alias not in VISION_CAPABLE and has_image(body):
|
if vision_alias and alias not in VISION_CAPABLE and has_image(body):
|
||||||
|
if _ist_coder_alias(alias) and await _bilder_fuer_coder_beschreiben(body, client, vision_alias):
|
||||||
|
routed = {"x-mc-routed-to": alias,
|
||||||
|
"x-mc-route-reason": "Bild beschrieben (Vision) -> bleibt beim Coder",
|
||||||
|
"x-mc-lane": routed.get("x-mc-lane", "-")}
|
||||||
|
else:
|
||||||
alias = vision_alias
|
alias = vision_alias
|
||||||
body["model"] = alias
|
body["model"] = alias
|
||||||
# HTTP-Header-Werte muessen latin-1 sein — kein '→' o.ae. (sonst 500, 13.07.).
|
# HTTP-Header-Werte muessen latin-1 sein — kein '→' o.ae. (sonst 500, 13.07.).
|
||||||
@@ -116,8 +179,6 @@ async def _proxy(path: str, request: Request):
|
|||||||
body["chat_template_kwargs"] = {"enable_thinking": False}
|
body["chat_template_kwargs"] = {"enable_thinking": False}
|
||||||
_inject_language(body, alias)
|
_inject_language(body, alias)
|
||||||
url = f"{LLAMA_SWAP_URL}{path}"
|
url = f"{LLAMA_SWAP_URL}{path}"
|
||||||
|
|
||||||
client = request.app.state.gw_client # geteilter Keep-Alive-Client (siehe app.py lifespan)
|
|
||||||
if body.get("stream"):
|
if body.get("stream"):
|
||||||
req = client.build_request("POST", url, json=body, timeout=None)
|
req = client.build_request("POST", url, json=body, timeout=None)
|
||||||
r = await client.send(req, stream=True)
|
r = await client.send(req, stream=True)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ _CODE_HINT = re.compile(
|
|||||||
|
|
||||||
# Bild-Weiche (Faden 11): Aliase, die selbst Bilder koennen — die werden NIE umgeroutet.
|
# Bild-Weiche (Faden 11): Aliase, die selbst Bilder koennen — die werden NIE umgeroutet.
|
||||||
VISION_CAPABLE = {"vision", "scout"}
|
VISION_CAPABLE = {"vision", "scout"}
|
||||||
_IMAGE_PART_TYPES = {"image_url", "input_image", "image"}
|
IMAGE_PART_TYPES = {"image_url", "input_image", "image"}
|
||||||
|
|
||||||
|
|
||||||
def has_image(body: dict) -> bool:
|
def has_image(body: dict) -> bool:
|
||||||
@@ -64,7 +64,7 @@ def has_image(body: dict) -> bool:
|
|||||||
content = m.get("content")
|
content = m.get("content")
|
||||||
if isinstance(content, list):
|
if isinstance(content, list):
|
||||||
for part in content:
|
for part in content:
|
||||||
if isinstance(part, dict) and part.get("type") in _IMAGE_PART_TYPES:
|
if isinstance(part, dict) and part.get("type") in IMAGE_PART_TYPES:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ hint=""
|
|||||||
ctx="$(cat <<EOF
|
ctx="$(cat <<EOF
|
||||||
[BOX-ORIENTIERUNG — du bist ein Hintergrund-Worker auf der lokalen AI-Box (Linux, Zeit UTC / lokal Europe/Berlin, locale de_DE), NICHT Lucy und kein Dev-Laptop. Du arbeitest EINE Kanban-Aufgabe ab.]
|
[BOX-ORIENTIERUNG — du bist ein Hintergrund-Worker auf der lokalen AI-Box (Linux, Zeit UTC / lokal Europe/Berlin, locale de_DE), NICHT Lucy und kein Dev-Laptop. Du arbeitest EINE Kanban-Aufgabe ab.]
|
||||||
Die Modelle / das "Hirn" laufen ausschliesslich ueber den Router llama-swap auf 127.0.0.1:8080 (startet je Modell einen llama-server) bzw. das OpenAI-kompatible Gateway von MC2 auf 127.0.0.1:9001/v1 — es gibt KEINEN anderen Modell-Server, also nicht danach suchen. Weitere Dienste: hermes-gateway :8642, mem0-Gedaechtnis :8765. Der MC2-Live-Checkout ~/mission-control-v2 ist nur zum LESEN da (Schreiben/committen TABU — Code-Arbeit immer im frischen Klon deines Task-Workspaces). Grenzen (was gehoert wohin): MC2 = Steuerpult (verwalten/klicken/zusehen, KEIN Chat-UI), Lucy = Chat & Stimme (eigenes Repo), Hermes-Quelle ist TABU (Agenten-Verhalten nur ueber Config/SOUL/Skill/Hook/MCP aendern). Feste Entscheide/Verbote + volle Grenzen-Landkarte: ~/mission-control-v2/docs/wissen/ (GRENZEN/VERDIKTE/FALLEN/STACK) + ~/wissens-vault/eigenbau-landkarte.md.${hint}
|
Die Modelle / das "Hirn" laufen ausschliesslich ueber den Router llama-swap auf 127.0.0.1:8080 (startet je Modell einen llama-server) bzw. das OpenAI-kompatible Gateway von MC2 auf 127.0.0.1:9001/v1 — es gibt KEINEN anderen Modell-Server, also nicht danach suchen. Weitere Dienste: hermes-gateway :8642, mem0-Gedaechtnis :8765. Der MC2-Live-Checkout ~/mission-control-v2 ist nur zum LESEN da (Schreiben/committen TABU — Code-Arbeit immer im frischen Klon deines Task-Workspaces). Grenzen (was gehoert wohin): MC2 = Steuerpult (verwalten/klicken/zusehen, KEIN Chat-UI), Lucy = Chat & Stimme (eigenes Repo), Hermes-Quelle ist TABU (Agenten-Verhalten nur ueber Config/SOUL/Skill/Hook/MCP aendern). Feste Entscheide/Verbote + volle Grenzen-Landkarte: ~/mission-control-v2/docs/wissen/ (GRENZEN/VERDIKTE/FALLEN/STACK) + ~/wissens-vault/eigenbau-landkarte.md.${hint}
|
||||||
|
PROTOKOLL-PFLICHT (15.07.): Beende deine Aufgabe IMMER mit dem Tool-Aufruf kanban_complete (fertig) oder kanban_block (Frage/Sackgasse). Eine Text-Zusammenfassung OHNE diesen Aufruf zaehlt als Absturz (Protokollverstoss) und blockiert die Karte — genau so ging am 14.07. eine fertige Arbeit verloren.
|
||||||
EOF
|
EOF
|
||||||
)"
|
)"
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,18 @@ ti = payload.get("tool_input") or {}
|
|||||||
home = os.path.realpath(os.path.expanduser("~"))
|
home = os.path.realpath(os.path.expanduser("~"))
|
||||||
LIVE = os.path.join(home, "mission-control-v2")
|
LIVE = os.path.join(home, "mission-control-v2")
|
||||||
SRC = os.path.join(home, ".hermes", "hermes-agent")
|
SRC = os.path.join(home, ".hermes", "hermes-agent")
|
||||||
|
# R2 (Review 15.07.): systemd-User-Units. Die Nacht-Werkstatt hat am 14.07. einen Override
|
||||||
|
# DIREKT scharf geschaltet — ging gut, verletzt aber "jede Code-Zeile ist Klick-pflichtig".
|
||||||
|
# Unit-/Override-Dateien werden nur noch ueber eine angenommene Karte installiert.
|
||||||
|
SYSD = os.path.join(home, ".config", "systemd", "user")
|
||||||
|
|
||||||
MSG = (
|
MSG = (
|
||||||
"TABU (Faden 8): Schreiben an dieser Stelle ist fuer Worker gesperrt. Der Live-Checkout "
|
"TABU (Faden 8/R2): Schreiben an dieser Stelle ist fuer Worker gesperrt. Der Live-Checkout "
|
||||||
"~/mission-control-v2 und die Hermes-Quelle ~/.hermes/hermes-agent duerfen NIE veraendert "
|
"~/mission-control-v2, die Hermes-Quelle ~/.hermes/hermes-agent und die systemd-User-Units "
|
||||||
"werden. Arbeite im FRISCHEN Klon deines Task-Workspaces; MC2-Aenderungen kommen als "
|
"~/.config/systemd/user duerfen NIE direkt veraendert werden. Arbeite im FRISCHEN Klon "
|
||||||
"Vorschlags-Branch (nie am Live-Checkout), Hermes-Verhalten nur ueber Config/SOUL/Skill/"
|
"deines Task-Workspaces; MC2-Aenderungen kommen als Vorschlags-Branch, systemd-Unit-/"
|
||||||
"Hook/MCP. Siehe docs/wissen/GRENZEN.md."
|
"Override-Dateien werden nur ueber eine angenommene Karte installiert, Hermes-Verhalten "
|
||||||
|
"nur ueber Config/SOUL/Skill/Hook/MCP. Siehe docs/wissen/GRENZEN.md."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -52,7 +57,7 @@ def block():
|
|||||||
|
|
||||||
def under_tabu(path):
|
def under_tabu(path):
|
||||||
rp = os.path.realpath(path)
|
rp = os.path.realpath(path)
|
||||||
return rp == LIVE or rp == SRC or rp.startswith(LIVE + os.sep) or rp.startswith(SRC + os.sep)
|
return any(rp == t or rp.startswith(t + os.sep) for t in (LIVE, SRC, SYSD))
|
||||||
|
|
||||||
|
|
||||||
# --- Datei-Tools: Zielpfad kanonisch pruefen (zuverlaessig) -------------------------
|
# --- Datei-Tools: Zielpfad kanonisch pruefen (zuverlaessig) -------------------------
|
||||||
@@ -78,6 +83,7 @@ if tool == "terminal":
|
|||||||
tabu_forms = [
|
tabu_forms = [
|
||||||
LIVE, "~/mission-control-v2", "$HOME/mission-control-v2", "${HOME}/mission-control-v2",
|
LIVE, "~/mission-control-v2", "$HOME/mission-control-v2", "${HOME}/mission-control-v2",
|
||||||
SRC, "~/.hermes/hermes-agent", "$HOME/.hermes/hermes-agent", "${HOME}/.hermes/hermes-agent",
|
SRC, "~/.hermes/hermes-agent", "$HOME/.hermes/hermes-agent", "${HOME}/.hermes/hermes-agent",
|
||||||
|
SYSD, "~/.config/systemd/user", "$HOME/.config/systemd/user", "${HOME}/.config/systemd/user",
|
||||||
]
|
]
|
||||||
tabu_alt = "(?:" + "|".join(re.escape(t) for t in tabu_forms) + ")"
|
tabu_alt = "(?:" + "|".join(re.escape(t) for t in tabu_forms) + ")"
|
||||||
if not re.search(tabu_alt, cmd):
|
if not re.search(tabu_alt, cmd):
|
||||||
|
|||||||
@@ -83,6 +83,25 @@ else
|
|||||||
FAILED+=("Voice (Lucys Sprech-Pfad)")
|
FAILED+=("Voice (Lucys Sprech-Pfad)")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── 4) Repo-Wächter: verlorene Merges (Vorfall 12.–15.07.2026) ──────────────
|
||||||
|
# Der Annahme-Runner merged+pusht nach origin/main; klemmt der Push (Gitea-Auth
|
||||||
|
# flattert) oder überschreibt eine fremde Session origin/main, trägt Box-main
|
||||||
|
# Commits, die origin fehlen — der nächste deploy-Reset VERNICHTET sie still
|
||||||
|
# (so verlor die Box die angenommene Karte 'wartung/lucy-annahme-fixes' vom
|
||||||
|
# 12.07., bemerkt erst beim Review am 15.07.). Hier täglich gegenprüfen.
|
||||||
|
LIVE_CO="$HOME/mission-control-v2"
|
||||||
|
if git -C "$LIVE_CO" fetch -q origin 2>/dev/null; then
|
||||||
|
LOST=$(git -C "$LIVE_CO" log --oneline origin/main..main 2>/dev/null | head -3 | tr '\n' ' ')
|
||||||
|
if [ -n "$LOST" ]; then
|
||||||
|
say "FAIL · Box-main trägt ungepushte Commits (Deploy würde sie vernichten): $LOST"
|
||||||
|
FAILED+=("Repo (ungepushte Commits auf Box-main: $LOST— vor dem nächsten Deploy nach origin retten)")
|
||||||
|
else
|
||||||
|
say "PASS · Repo-Wächter (Box-main == origin/main)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
say "SKIP · Repo-Wächter (git fetch fehlgeschlagen)"
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Meldung nur bei Rot ──────────────────────────────────────────────────────
|
# ── Meldung nur bei Rot ──────────────────────────────────────────────────────
|
||||||
if [ "${#FAILED[@]}" -eq 0 ]; then
|
if [ "${#FAILED[@]}" -eq 0 ]; then
|
||||||
say "Alle Selbst-Tests grün ($(date '+%F %H:%M'))."
|
say "Alle Selbst-Tests grün ($(date '+%F %H:%M'))."
|
||||||
|
|||||||
@@ -86,10 +86,12 @@ Bevor du selbst Tasks aufteilst, befragst du zwingend **`gpt-oss-120b`** nach ei
|
|||||||
3. Fuer jeden Teil-Task entscheide: (a) Artefakt, (b) Worker (`Qwen3-Coder-Next` + `build`/`refactor`), (c) Kontext.
|
3. Fuer jeden Teil-Task entscheide: (a) Artefakt, (b) Worker (`Qwen3-Coder-Next` + `build`/`refactor`), (c) Kontext.
|
||||||
|
|
||||||
### 2. Worktree anlegen (Slug = kurzer Kebab-Case-Name des Auftrags)
|
### 2. Worktree anlegen (Slug = kurzer Kebab-Case-Name des Auftrags)
|
||||||
|
Worktrees liegen unter `~/.hermes/worktrees/` (NICHT /tmp — ueberlebt keinen Reboot und
|
||||||
|
hinterlaesst kaputte Worktree-Registrierungen, Review-Befund 15.07.):
|
||||||
```
|
```
|
||||||
cd ~/mission-control-v2 && git fetch -q origin \
|
mkdir -p ~/.hermes/worktrees && cd ~/mission-control-v2 && git fetch -q origin \
|
||||||
&& git worktree add /tmp/orch-<slug> -b orchestrator/<slug> origin/main
|
&& git worktree add ~/.hermes/worktrees/orch-<slug> -b orchestrator/<slug> origin/main
|
||||||
mkdir -p /tmp/orch-<slug>-work # Ablage fuer rohe Worker-Ausgaben
|
mkdir -p ~/.hermes/worktrees/orch-<slug>-work # Ablage fuer rohe Worker-Ausgaben
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Routen — je Teil-Task ein Worker-Ein-Schuss (seriell)
|
### 3. Routen — je Teil-Task ein Worker-Ein-Schuss (seriell)
|
||||||
@@ -98,19 +100,19 @@ Fuer jeden Bau-Teil-Task:
|
|||||||
fertige Artefakte aus frueheren Schritten als Kontext mitgeben, wenn der Worker sie braucht.
|
fertige Artefakte aus frueheren Schritten als Kontext mitgeben, wenn der Worker sie braucht.
|
||||||
2. Ruf den Worker auf und fang die Ausgabe roh ab:
|
2. Ruf den Worker auf und fang die Ausgabe roh ab:
|
||||||
```
|
```
|
||||||
printf '%s' "$eingabe" | WORKER_ROLE=build ~/.hermes/scripts/worker.sh > /tmp/orch-<slug>-work/<n>.out
|
printf '%s' "$eingabe" | WORKER_ROLE=build ~/.hermes/scripts/worker.sh > ~/.hermes/worktrees/orch-<slug>-work/<n>.out
|
||||||
```
|
```
|
||||||
(Default-Modell ist `Qwen3-Coder-Next`. Fuer einen bewusst anderen Worker `WORKER_MODEL=<echte-id>`.)
|
(Default-Modell ist `Qwen3-Coder-Next`. Fuer einen bewusst anderen Worker `WORKER_MODEL=<echte-id>`.)
|
||||||
3. **Pruefe die Ausgabe, bevor du sie verwendest:** Beginnt sie mit `FEHLT:`, hast du zu wenig
|
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
|
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
|
oder Prosa drumherum gesetzt, entferne ihn. Dann schreibe das saubere Artefakt mit deinen
|
||||||
Datei-Tools an seinen Platz im Worktree (`/tmp/orch-<slug>/...`). Der Worker hat KEINE
|
Datei-Tools an seinen Platz im Worktree (`~/.hermes/worktrees/orch-<slug>/...`). Der Worker hat KEINE
|
||||||
Datei-Haende — das Schreiben machst DU.
|
Datei-Haende — das Schreiben machst DU.
|
||||||
4. **Schreibziel-Pflicht (Vorfall 09.07.2026):** JEDES write_file/patch-Ziel MUSS mit
|
4. **Schreibziel-Pflicht (Vorfall 09.07.2026):** JEDES write_file/patch-Ziel MUSS mit
|
||||||
`/tmp/orch-<slug>/` beginnen — vor dem ersten Schreiben einmal laut pruefen. NIEMALS nach
|
`~/.hermes/worktrees/orch-<slug>/` beginnen — vor dem ersten Schreiben einmal laut pruefen. NIEMALS nach
|
||||||
`~/mission-control-v2/...` schreiben (Live-Checkout!), NIEMALS Work-Dirs/Worktrees FREMDER
|
`~/mission-control-v2/...` schreiben (Live-Checkout!), NIEMALS Work-Dirs/Worktrees FREMDER
|
||||||
Slugs wiederverwenden (beim Doku-Lauf landete ein Artefakt im Live-Checkout, weil das alte
|
Slugs wiederverwenden (beim Doku-Lauf landete ein Artefakt im Live-Checkout, weil das alte
|
||||||
`/tmp/orch-hermes-desktop-work` recycelt wurde). Existiert dein `/tmp/orch-<slug>` noch nicht,
|
`/tmp/orch-hermes-desktop-work` recycelt wurde). Existiert dein `~/.hermes/worktrees/orch-<slug>` noch nicht,
|
||||||
ist das der Beweis, dass Schritt 2 fehlt — erst Worktree anlegen.
|
ist das der Beweis, dass Schritt 2 fehlt — erst Worktree anlegen.
|
||||||
5. **Grosse Artefakte NIE im Klartext in deine Antwort** — sie gehoeren in Dateien im Worktree.
|
5. **Grosse Artefakte NIE im Klartext in deine Antwort** — sie gehoeren in Dateien im Worktree.
|
||||||
Wer Dateiinhalte in die Antwort kippt, stirbt am Output-Limit mitten im Lauf (Doku-Lauf
|
Wer Dateiinhalte in die Antwort kippt, stirbt am Output-Limit mitten im Lauf (Doku-Lauf
|
||||||
@@ -148,7 +150,7 @@ Grund wie in der Werkstatt („wer seine eigenen Hausaufgaben benotet, stimmt si
|
|||||||
**Zuerst ALLES stagen** — sonst fehlen NEUE Dateien im Diff (haeufigste Falle: `git diff` ignoriert
|
**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):
|
untracked Dateien, die Kritiker bekaemen einen LEEREN Diff und wuerden alles blind ablehnen):
|
||||||
```
|
```
|
||||||
git -C /tmp/orch-<slug> add -A
|
git -C ~/.hermes/worktrees/orch-<slug> add -A
|
||||||
```
|
```
|
||||||
Dann die Eingabe EINMAL bauen (mit `--cached`, damit die neuen Dateien drin sind):
|
Dann die Eingabe EINMAL bauen (mit `--cached`, damit die neuen Dateien drin sind):
|
||||||
```
|
```
|
||||||
@@ -159,7 +161,7 @@ ZERLEGUNG (deine Teil-Tasks, je 1 Zeile):
|
|||||||
<1..n>
|
<1..n>
|
||||||
|
|
||||||
GIT-DIFF des Worktrees:
|
GIT-DIFF des Worktrees:
|
||||||
$(git -C /tmp/orch-<slug> diff --cached origin/main)"
|
$(git -C ~/.hermes/worktrees/orch-<slug> diff --cached origin/main)"
|
||||||
```
|
```
|
||||||
Ist dieser Diff LEER, hast du nicht gestaged (oder nichts gebaut) → NICHT weiter, erst beheben.
|
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):
|
**Kritik A — Kompetenz** (`Qwen3-Coder-Next`, starker Coder, vom Bauen noch geladen → KEIN Swap):
|
||||||
@@ -212,7 +214,7 @@ Zugriff aufs Repo. Er sieht NUR, was du ihm auf stdin gibst. Deshalb:
|
|||||||
den Fokus. Ziel: das kleinste, das den Teil-Task eindeutig macht.
|
den Fokus. Ziel: das kleinste, das den Teil-Task eindeutig macht.
|
||||||
|
|
||||||
## Nach dem Commander-Entscheid (kommt als neuer Auftrag)
|
## Nach dem Commander-Entscheid (kommt als neuer Auftrag)
|
||||||
- „verwerfen" → `git worktree remove /tmp/orch-<slug> --force && git branch -D orchestrator/<slug>`
|
- „verwerfen" → `git worktree remove ~/.hermes/worktrees/orch-<slug> --force && git branch -D orchestrator/<slug>`
|
||||||
(+ `rm -rf /tmp/orch-<slug>-work`; Remote-Branch loeschen, falls gepusht).
|
(+ `rm -rf ~/.hermes/worktrees/orch-<slug>-work`; Remote-Branch loeschen, falls gepusht).
|
||||||
- „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es nur dort).
|
- „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es nur dort).
|
||||||
Du pushst NIE nach main.
|
Du pushst NIE nach main.
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ unangenommene Branches aufbauen — erst wenn die Vor-Etappe in main ist, kommt
|
|||||||
## Ablauf
|
## Ablauf
|
||||||
|
|
||||||
1. **Worktree anlegen** (Slug = kurzer Kebab-Case-Name des Auftrags):
|
1. **Worktree anlegen** (Slug = kurzer Kebab-Case-Name des Auftrags):
|
||||||
`cd ~/mission-control-v2 && git fetch origin && git worktree add /tmp/wartung-<slug> -b wartung/<slug> origin/main`
|
`mkdir -p ~/.hermes/worktrees && cd ~/mission-control-v2 && git fetch origin && git worktree add ~/.hermes/worktrees/wartung-<slug> -b wartung/<slug> origin/main`
|
||||||
|
(Worktrees NIE unter /tmp — ueberlebt keinen Reboot, Review-Befund 15.07.)
|
||||||
2. **Patch** nur im Worktree. Minimal-invasiv, Stil der umgebenden Datei übernehmen
|
2. **Patch** nur im Worktree. Minimal-invasiv, Stil der umgebenden Datei übernehmen
|
||||||
(deutsche Kommentare, bestehende Muster).
|
(deutsche Kommentare, bestehende Muster).
|
||||||
3. **Selbst-Gate** (was zutrifft):
|
3. **Selbst-Gate** (was zutrifft):
|
||||||
@@ -76,7 +77,7 @@ unangenommene Branches aufbauen — erst wenn die Vor-Etappe in main ist, kommt
|
|||||||
<der Auftrag>
|
<der Auftrag>
|
||||||
|
|
||||||
GIT-DIFF des Worktrees:
|
GIT-DIFF des Worktrees:
|
||||||
<git -C /tmp/wartung-<slug> diff origin/main>" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh
|
<git -C ~/.hermes/worktrees/wartung-<slug> diff origin/main>" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Das läuft auf `Qwen3-Coder-Next` (128k, code-stark, anderes Modell als der Qwen3.6-Worker,
|
Das läuft auf `Qwen3-Coder-Next` (128k, code-stark, anderes Modell als der Qwen3.6-Worker,
|
||||||
@@ -113,7 +114,7 @@ unangenommene Branches aufbauen — erst wenn die Vor-Etappe in main ist, kommt
|
|||||||
|
|
||||||
## Nach dem User-Entscheid (kommt als neuer Auftrag)
|
## Nach dem User-Entscheid (kommt als neuer Auftrag)
|
||||||
|
|
||||||
- „verwerfen" → `git worktree remove /tmp/wartung-<slug> --force && git branch -D wartung/<slug>`
|
- „verwerfen" → `git worktree remove ~/.hermes/worktrees/wartung-<slug> --force && git branch -D wartung/<slug>`
|
||||||
(+ Remote-Branch löschen, falls gepusht: `git push origin --delete wartung/<slug>`)
|
(+ Remote-Branch löschen, falls gepusht: `git push origin --delete wartung/<slug>`)
|
||||||
- „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es
|
- „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es
|
||||||
nur dort). Du pushst NIE nach main — auch nicht mit Token.
|
nur dort). Du pushst NIE nach main — auch nicht mit Token.
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
diff --git a/gateway/platforms/api_server.py b/gateway/platforms/api_server.py
|
||||||
|
index 628713224..415f3b2dc 100644
|
||||||
|
--- a/gateway/platforms/api_server.py
|
||||||
|
+++ b/gateway/platforms/api_server.py
|
||||||
|
@@ -362,6 +362,159 @@ def _multimodal_validation_error(exc: ValueError, *, param: str) -> "web.Respons
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
+def _is_coder_model(model_name: Optional[str]) -> bool:
|
||||||
|
+ """Return True if model_name looks like a coder model (case-insensitive 'coder' in name)."""
|
||||||
|
+ if not model_name:
|
||||||
|
+ return False
|
||||||
|
+ return "coder" in str(model_name).lower()
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _extract_image_urls_from_messages(messages: List[Dict[str, Any]]) -> List[str]:
|
||||||
|
+ """Extract all image URLs from OpenAI-style messages payload."""
|
||||||
|
+ image_urls: List[str] = []
|
||||||
|
+ for msg in messages:
|
||||||
|
+ if not isinstance(msg, dict):
|
||||||
|
+ continue
|
||||||
|
+ content = msg.get("content")
|
||||||
|
+ if not content:
|
||||||
|
+ continue
|
||||||
|
+ # Normalize multimodal content to list of parts
|
||||||
|
+ parts: Any = content
|
||||||
|
+ if isinstance(content, str):
|
||||||
|
+ continue # text-only message has no images
|
||||||
|
+ if not isinstance(parts, list):
|
||||||
|
+ continue
|
||||||
|
+ for part in parts:
|
||||||
|
+ if not isinstance(part, dict):
|
||||||
|
+ continue
|
||||||
|
+ part_type = str(part.get("type", "")).strip().lower()
|
||||||
|
+ if part_type in {"image_url", "input_image"}:
|
||||||
|
+ img_ref = part.get("image_url")
|
||||||
|
+ if isinstance(img_ref, dict):
|
||||||
|
+ url = img_ref.get("url")
|
||||||
|
+ else:
|
||||||
|
+ url = img_ref
|
||||||
|
+ if isinstance(url, str) and url.strip():
|
||||||
|
+ image_urls.append(url.strip())
|
||||||
|
+ return image_urls
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+async def _maybe_enrich_with_vision_if_coder(
|
||||||
|
+ body: Dict[str, Any],
|
||||||
|
+) -> Dict[str, Any]:
|
||||||
|
+ """
|
||||||
|
+ For coder model requests with image attachments, pre-analyze images
|
||||||
|
+ with the vision fallback model and replace image parts with descriptive text.
|
||||||
|
+
|
||||||
|
+ Returns a modified copy of body with image URLs replaced by text
|
||||||
|
+ descriptions in the messages array.
|
||||||
|
+ """
|
||||||
|
+ model_name = body.get("model")
|
||||||
|
+ if not _is_coder_model(model_name):
|
||||||
|
+ return body
|
||||||
|
+
|
||||||
|
+ messages = body.get("messages")
|
||||||
|
+ if not messages or not isinstance(messages, list):
|
||||||
|
+ return body
|
||||||
|
+
|
||||||
|
+ image_urls = _extract_image_urls_from_messages(messages)
|
||||||
|
+ if not image_urls:
|
||||||
|
+ return body
|
||||||
|
+
|
||||||
|
+ # Import here to avoid circular dependency issues
|
||||||
|
+ try:
|
||||||
|
+ from tools.vision_tools import vision_analyze_tool
|
||||||
|
+ except ImportError:
|
||||||
|
+ logger.warning(
|
||||||
|
+ "Vision tool not available for coder model enrichment. "
|
||||||
|
+ "Install hermes-agent tools to enable this feature."
|
||||||
|
+ )
|
||||||
|
+ return body
|
||||||
|
+
|
||||||
|
+ # Use the configured vision model or fall back to VL-30B
|
||||||
|
+ # We'll let vision_analyze_tool use its own default (auxiliary.vision)
|
||||||
|
+ # but we can optionally override by passing model parameter
|
||||||
|
+ # For now, let it auto-resolve via config
|
||||||
|
+ analysis_prompt = (
|
||||||
|
+ "Describe everything visible in this image in thorough detail. "
|
||||||
|
+ "Include any text, code, data, objects, people, layout, colors, "
|
||||||
|
+ "and any other notable visual information."
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ # Build a mapping of URL -> description
|
||||||
|
+ url_to_desc: Dict[str, str] = {}
|
||||||
|
+ for img_url in image_urls:
|
||||||
|
+ try:
|
||||||
|
+ result_json = await vision_analyze_tool(
|
||||||
|
+ image_url=img_url,
|
||||||
|
+ user_prompt=analysis_prompt,
|
||||||
|
+ )
|
||||||
|
+ result = json.loads(result_json)
|
||||||
|
+ if result.get("success"):
|
||||||
|
+ desc = result.get("analysis", "")
|
||||||
|
+ url_to_desc[img_url] = (
|
||||||
|
+ f"[The user sent an image~ Here's what I can see:\n{desc}]\n"
|
||||||
|
+ f"[If you need a closer look, use vision_analyze with image_url: {img_url} ~]"
|
||||||
|
+ )
|
||||||
|
+ else:
|
||||||
|
+ url_to_desc[img_url] = (
|
||||||
|
+ "[The user sent an image but I couldn't quite see it this time (>_<) "
|
||||||
|
+ f"You can try looking at it yourself with vision_analyze using image_url: {img_url}]"
|
||||||
|
+ )
|
||||||
|
+ except Exception as e:
|
||||||
|
+ logger.error("Vision auto-analysis error for image %s: %s", img_url, e)
|
||||||
|
+ url_to_desc[img_url] = (
|
||||||
|
+ f"[The user sent an image but something went wrong when I tried to look at it~ "
|
||||||
|
+ f"You can try examining it yourself with vision_analyze using image_url: {img_url}]"
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ # Now replace image parts with text in a copy of messages
|
||||||
|
+ enriched_messages: List[Dict[str, Any]] = []
|
||||||
|
+ for msg in messages:
|
||||||
|
+ msg_copy = dict(msg)
|
||||||
|
+ content = msg_copy.get("content")
|
||||||
|
+ if isinstance(content, str):
|
||||||
|
+ enriched_messages.append(msg_copy)
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ if not isinstance(content, list):
|
||||||
|
+ enriched_messages.append(msg_copy)
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ enriched_parts: List[Any] = []
|
||||||
|
+ for part in content:
|
||||||
|
+ if not isinstance(part, dict):
|
||||||
|
+ enriched_parts.append(part)
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ part_type = str(part.get("type", "")).strip().lower()
|
||||||
|
+ if part_type in {"image_url", "input_image"}:
|
||||||
|
+ img_ref = part.get("image_url")
|
||||||
|
+ if isinstance(img_ref, dict):
|
||||||
|
+ img_url = img_ref.get("url")
|
||||||
|
+ else:
|
||||||
|
+ img_url = img_ref
|
||||||
|
+ if isinstance(img_url, str) and img_url in url_to_desc:
|
||||||
|
+ # Replace image part with text part
|
||||||
|
+ enriched_parts.append({
|
||||||
|
+ "type": "text",
|
||||||
|
+ "text": url_to_desc[img_url],
|
||||||
|
+ })
|
||||||
|
+ else:
|
||||||
|
+ # Keep original if URL not found (shouldn't happen)
|
||||||
|
+ enriched_parts.append(part)
|
||||||
|
+ else:
|
||||||
|
+ enriched_parts.append(part)
|
||||||
|
+
|
||||||
|
+ msg_copy["content"] = enriched_parts
|
||||||
|
+ enriched_messages.append(msg_copy)
|
||||||
|
+
|
||||||
|
+ # Return modified body
|
||||||
|
+ body_copy = dict(body)
|
||||||
|
+ body_copy["messages"] = enriched_messages
|
||||||
|
+ return body_copy
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def _session_chat_user_message(body: Dict[str, Any], *, param: str = "message") -> tuple[Any, Optional["web.Response"]]:
|
||||||
|
"""Parse and normalize session chat ``message`` / ``input`` like chat completions."""
|
||||||
|
user_message = body.get("message") or body.get("input")
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# UMBAUPLAN — Abschluss-Review 15.07.2026
|
||||||
|
|
||||||
|
**Was das ist:** Ergebnis des kompletten Projekt-Reviews (Repo + Box live + tagesaktuelle
|
||||||
|
Recherche). DIE Arbeitsliste für die Zeit nach Claude. Leser: der User, die Box-Worker
|
||||||
|
(über Queue-Karten) und Gemini/Antigravity (liest F:\-Checkout).
|
||||||
|
**Regel:** Erledigtes hier streichen. Neue Erkenntnisse einarbeiten, nicht daneben legen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0 · IST-Befund (live verifiziert 15.07., 09:20–11:00)
|
||||||
|
|
||||||
|
**Die Box ist gesund und die Autonomie-Kette ARBEITET nachweislich.**
|
||||||
|
|
||||||
|
- Dienste: alle aktiv (mission-control-2, hermes-gateway, hermes-builtin-ui, mem0, voice, box-console, llama-swap). 8 Tage Uptime, 68 GB frei.
|
||||||
|
- Nacht-Crons 15.07. alle grün: Traum 03:15 · Selbst-Inventur 03:35 · Idle-Radar 03:45 · Karten-Gutachter 04:00 · Bagatell 04:10 · Chef-Gutachter 04:30 · Daily-Briefing 08:00 · Release-Radar Mo 05:15. Backups: mc2-backup 03:32 + PBS 03:50 gelaufen.
|
||||||
|
- Versionen: llama.cpp **b9994** (Vulkan) · llama-swap **v239** · Hermes **0.18.2** (+1 legitimer carried commit) · MC2 2.0.0-w8.
|
||||||
|
- Warm-Set schlank: Hirn 17,5 GB + embed 2 GB + reranker 2,2 GB ≈ 22 GB.
|
||||||
|
- Repo: lokal = Gitea = Box @ main. Live-Checkout wieder sauber auf `main`.
|
||||||
|
- **Autonomie-Beweis der Nacht 14.07.:** Idle-Radar erhob Journal-Befund (stop-sigterm-Timeout) → Decomposer zerlegte → werkstatt baute Restart-Skript+systemd-Override → betrieb testete live (~6 s Recovery) → done. Ohne Menschen, ohne Claude.
|
||||||
|
|
||||||
|
## 1 · Heute im Review erledigt (15.07.)
|
||||||
|
|
||||||
|
- ✅ **Hermes-Quelle wieder jungfräulich:** verwaister ~160-Zeilen-Patch in `gateway/platforms/api_server.py` entfernt (Bild-Beschreibung für Coder — war NIE aufgerufen = toter Code, wurde aber von jedem `hermes update` mitgeschleppt = Konflikt-Risiko fürs kommende v0.19). Original archiviert: `docs/archiv/hermes-api_server-vision-patch-verwaist.diff`.
|
||||||
|
- ✅ **Idee daraus regelkonform portiert → Bild-Weiche v2** (MC2-Gateway): Bild-Request an Coder-Ziel bekommt Vision-BESCHREIBUNG injiziert und bleibt beim Coder (Screenshot-Debugging!); Fallback = alte Umleitung. **Branch `wartung/bild-weiche-v2` → wartet als Karte auf Klick.**
|
||||||
|
- ✅ **VL-8B-Leiche gefixt:** `agents.vision.model: Qwen3-VL-8B-Instruct` (Modell existiert seit 07.07. nicht mehr!) → `vision`-Alias, in Top-Level- UND beiden Profil-Configs (Backups `*.bak-review-20260715`). Gateway neu, `hermes doctor` grün.
|
||||||
|
- ✅ **Live-Checkout aufgeräumt:** zurück auf `main` (stand auf verwaistem Branch), 2 stale Branches gelöscht, leere `state.db` + alter `backend/memory.py` entfernt.
|
||||||
|
- ✅ **2 Mess-Karten in der Queue:** `t_5ada48ea` (Gemma-4-Hirn-Bench) + `t_64a384b9` (heavy-64k-Messung).
|
||||||
|
- ✅ Blockierte Karte `t_8231d6b8` analysiert: **Arbeit ist fertig+live** — Worker vergaß nur den `kanban_complete`-Aufruf. Abschluss = Sofort-Paket a).
|
||||||
|
|
||||||
|
## 2 · Konfig-Bewertung: Das ist GUT so (nicht anfassen)
|
||||||
|
|
||||||
|
| Bereich | Urteil |
|
||||||
|
|---|---|
|
||||||
|
| Modell-Kader (8 Modelle, klare Rollen, schlankes Warm-Set) | ✅ passt zur 122-GB-Box; Recherche 15.07.: **kein** Qwen-Coder-Nachfolger draußen, Coder-Verdikt hält |
|
||||||
|
| Vulkan/RADV statt ROCm | ✅ hält (ROCm auf gfx1151 weiter „Preview", llama.cpp-Issue #21284 offen) |
|
||||||
|
| Fundament-Fixes (concurrency 1, auto_decompose true, Specifier-Pins, 3 Hooks je Profil) | ✅ live korrekt verifiziert |
|
||||||
|
| Verdikte Mem0 / Telegram / Electron / pocket-Default / Hermes-Prompts englisch | ✅ bleiben |
|
||||||
|
| DeepSeek V4-Flash als Kandidat | ❌ zu groß (103–142 GB Quant) — passt nie neben das Warm-Set |
|
||||||
|
| systemd-Override Restart=always für MC2 | ✅ behalten (User-Entscheid 15.07.) — gute Appliance-Praxis |
|
||||||
|
|
||||||
|
## 3 · SOFORT-Paket (User-Klicks + eine sudo-Runde)
|
||||||
|
|
||||||
|
a) **Karte `wartung/bild-weiche-v2` annehmen** (Auftragsbuch). Danach prüft man so:
|
||||||
|
Bild an `model:"coder"` schicken → Antwort-Header `x-mc-route-reason: Bild beschrieben (Vision) -> bleibt beim Coder`.
|
||||||
|
b) **Karte `t_8231d6b8` abschließen** (Arbeit war fertig, nur Protokollverstoß):
|
||||||
|
`ssh hitonabi@192.168.178.151` → `cd ~/.hermes/hermes-agent && venv/bin/hermes kanban complete t_8231d6b8 --comment "war fertig+verifiziert, Worker vergass complete-Call"`
|
||||||
|
c) **sudo-Runde (einmal Passwort):**
|
||||||
|
1. Stale v1-Unit weg: `sudo systemctl disable mission-control.service; sudo rm /etc/systemd/system/mission-control.service; sudo rm -rf /opt/mission-control` (Faden C14)
|
||||||
|
2. Root-Warmup-Kopie aktualisieren: `sudo install -m 0755 ~/mission-control-v2/deploy/warmup.sh /usr/local/bin/llama-swap-warmup.sh` (stale seit 07.07. — sonst wärmt ein llama-swap-Restart falsch)
|
||||||
|
d) **Daily-Briefing-Cron** (dein eigener, 08:00): steht auf `Repeat: 9/41` — **läuft nach 41 Läufen aus**. Wenn dauerhaft gewollt: auf ∞ stellen (`hermes cron edit 095d53c1e99e`).
|
||||||
|
|
||||||
|
## 4 · ROBUSTHEIT (Priorität 1 — Karten für die Queue)
|
||||||
|
|
||||||
|
- **R1 · Worker-Protokoll-Erinnerung** (klein): `box-steckbrief-inject.sh` um einen Satz ergänzen: „Am Ende IMMER kanban_complete oder kanban_block aufrufen — sauberes Text-Ende zählt nicht." Genau daran blockierte die Nacht-Karte.
|
||||||
|
- **R2 · Guard-Erweiterung systemd** (klein, braucht User-Ja): `tabu-pfade-guard.py` um `~/.config/systemd/user/` erweitern. Die Nacht-Werkstatt hat den Override direkt scharf geschaltet — ging gut, widerspricht aber „jede Code-Zeile ist Klick-pflichtig". Installation künftig nur über angenommene Karte.
|
||||||
|
- **R3 · Hermes v0.19 kommt** (Sammel-Release, ~660 PRs seit v0.18.0): Fangnetz existiert (autoupdate → doctor → postcheck → Auto-Rollback+Pin+Telegram). Quelle ist seit heute konfliktfrei. **Nichts zu tun — nur wissen, dass die Meldung kommen wird.**
|
||||||
|
- **R4 · Orchestrator-Worktrees raus aus /tmp** (klein): `/tmp/orch-kanten` überlebt keinen Reboot; Worktree-Basis nach `~/.hermes/orch-worktrees/` o. ä.
|
||||||
|
- **R5 · Runbook C13** (größter offener Robustheits-Batzen): 1-Seiter „Box tot → was drücken" + Bare-Metal-Bootstrap (docs/DISASTER_RECOVERY.md existiert als Konzept). Karten-Serie.
|
||||||
|
- **R6 · Backup-Restposten:** QNAP-/pve-root-Passwörter in Keepass verifizieren; Restore-Probe einmal fahren (PBS läuft, aber ungeprobt zurückgespielt).
|
||||||
|
|
||||||
|
## 5 · AUFRÄUMEN (Priorität 2)
|
||||||
|
|
||||||
|
- **A1 · Alt-Docs** (Bagatell-Klasse, Box darf selbst): README/STATUS/CUTOVER/HERMES_SETUP tragen Vor-Trennung-Stand (Faden C15).
|
||||||
|
- **A2 · Lucy-Repo-Hygiene** (PC-Karte über Lucy-Pipeline): 4 unkommittierte Dateien (Hybrid-TTS-Routing in App.tsx/useVoiceAgent/voice-core/kokoro_server) committen oder verwerfen — vorher prüfen, was live läuft; Saber-Altlasten löschen (XTTS-Datensatz-Verdikt: unbrauchbar); untracked venvs/Datasets ignorieren oder weg.
|
||||||
|
- **A3 · Hermes-Home-Kleinkram:** `hermes_cli/web_dist.bak-rootbuild/` + `.install_method` (harmlos, bei Gelegenheit).
|
||||||
|
- **A4 · D16-Restposten:** c) System-Logs ausbauen (Fehler-Filter) · e) Mem0-Dubletten automatisch statt Knopf · f) Lucy-Reste aus Web-UI + Voice-Sidecar-Diät. Alles einzelne Queue-Karten.
|
||||||
|
|
||||||
|
## 6 · FEATURES (Priorität 3)
|
||||||
|
|
||||||
|
- **F1 · Bild-Weiche v2:** gebaut, wartet auf Klick (Sofort-Paket a).
|
||||||
|
- **F2 · heavy auf 64k** (nach Messkarte `t_64a384b9` + User-Ja): dann `delegation`-Floor erfüllt → heavy als delegate_task-Ziel freischalten → Konzept-Fließband/Lucy nutzen heavy nativ statt worker.sh-Umweg.
|
||||||
|
- **F3 · Mini-Stimme v2 (AKTIV, User-Entscheid 15.07.):**
|
||||||
|
1. User: Colab-Volllauf (~3 h, Notebook `Lucy_Stimme_Training_v2.ipynb` auf Desktop, Volltraining-Modus ist schon eingestellt)
|
||||||
|
2. Danach: Modell-Datei tauschen + Whisper-Rücktranskriptions-Test (der EINZIGE ehrliche Richter) — kann PC-Hermes über die Lucy-Pipeline
|
||||||
|
3. A/B gegen pocket im Live-Gespräch → Default-Entscheid
|
||||||
|
4. **Zukunftsfaden v3:** Qwen3-TTS-**Instruct** kann inzwischen Stil/Emotion bei geklonten Stimmen steuern — genau das „alles klingt gleich"-Problem. Neuer Datensatz mit Emotions-Instruktionen, sonst gleiches Rezept.
|
||||||
|
- **F4 · Gemma 4 26B-A4B als Hirn-Kandidat** (nach Bench-Karte `t_5ada48ea`): Community meldet ~110 t/s mit MTP-Head (vs. ~66 heute), multimodal (könnte Hirn+Augen VEREINEN), 256k Kontext. ABER: gemma-Vorgeschichte (Cache-Bug #21468, „Fix" unbestätigt), KV nur f16 (RAM!), mmproj×MTP ungeklärt. **Nur mit Messbeweis + User-Ja — Hirn-Swap ist ein Groß-Ereignis mit Rollback-Plan.**
|
||||||
|
|
||||||
|
## 7 · TEMPO (Priorität 4)
|
||||||
|
|
||||||
|
- **T1 · ROCm-Recheck 20.–25.07.** (Box-Reminder #3 existiert): Stand 15.07. hält Vulkan (tg-Führung bestätigt, #21284 weiter offen). Falls ROCm 7.13+ liefert: NUR Hybrid für heavy/coder-Prefill erwägen, **Hirn bleibt Vulkan**.
|
||||||
|
- **T2 · hipEngine-Watch** (Reminder #4 existiert): weiterhin praktisch Qwen3.6-only, gfx1151 nur „initial pass" — beobachten, nicht adoptieren.
|
||||||
|
- **T3 · Faden 10** (spec-draft-Sweep): bleibt bewusst übersprungen (User 14.07.).
|
||||||
|
|
||||||
|
## 8 · Übergabe-Logik (wenn Claude weg ist)
|
||||||
|
|
||||||
|
1. **Neue Ideen** → eine der drei Türen (Telegram / Lucy / MC2-Ideenfeld) → Queue → Karte → dein Klick. Die Kette ist E2E bewiesen.
|
||||||
|
2. **Wissen** → `docs/wissen/` (dieser Plan, STACK, FALLEN, GRENZEN, VERDIKTE) — Box liest `~/mission-control-v2`, Gemini liest `F:\Coding Stuff\mission-control-2`. GEMINI_BRIEFING.md existiert.
|
||||||
|
3. **Pflege** → Auto-Update-Timer mit Rollback+Pin; Selfsmoke täglich; Alarmkette Telegram.
|
||||||
|
4. **Evolution** → Release-Radar (Mo) + Chef-Gutachter (täglich) + Traum (täglich) finden Chancen; Bench-Harnesses (brain-bench, model-bench) messen sie; du entscheidest per Klick.
|
||||||
|
5. **Notfall** → Gemini/Antigravity als Review-Partner (RUNBOOK „Wann Gemini rufen").
|
||||||
|
|
||||||
|
## 9 · Recherche-Quellen (15.07.2026)
|
||||||
|
|
||||||
|
- Strix-Halo-Benchmarks/Grid: [strix-halo-guide](https://github.com/hogeheer499-commits/strix-halo-guide) · [llm-tracker Strix Halo](https://llm-tracker.info/_TOORG/Strix-Halo)
|
||||||
|
- ROCm-Prefill-Issue: [llama.cpp #21284](https://github.com/ggml-org/llama.cpp/issues/21284) (offen)
|
||||||
|
- Gemma 4: [26B-A4B auf Strix Halo](https://akehir.com/blog/strix-halo-kubernetes-llm-gemma-4) · [Cache-Bug #21468](https://github.com/ggml-org/llama.cpp/issues/21468) · [unsloth GGUF](https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF) · [AMD Day-0](https://www.amd.com/en/developer/resources/technical-articles/2026/day-0-support-for-gemma-4-on-amd-processors-and-gpus.html)
|
||||||
|
- DeepSeek V4: [unsloth Doku](https://unsloth.ai/docs/models/deepseek-v4) (103–142 GB → zu groß)
|
||||||
|
- Hermes v0.19: [Releases](https://github.com/NousResearch/hermes-agent/releases)
|
||||||
|
- hipEngine: [shisa-ai/hipEngine](https://github.com/shisa-ai/hipEngine)
|
||||||
|
- Qwen3-TTS (Instruct/Emotion): [Qwen-Blog](https://qwen.ai/blog?id=qwen3tts-0115) · [Emotion-Diskussion](https://github.com/QwenLM/Qwen3-TTS/discussions/218)
|
||||||
Reference in New Issue
Block a user