Autonomie 0d+0g: Lucy-Alarm-Weg, taegliche Selbst-Smokes, Selbstreparatur nach rotem Update

- /api/alarm: Lucy-unabhaengiger Telegram-Weg (fuer den PC-Lucy-Watchdog, wenn die App haengt).
- self-smoke.sh + mc2-selfsmoke.timer (taeglich 07:15): Gateway/Tools/Voice aktiv durchspielen,
  Meldung nur bei Rot. Ergaenzt den passiven Health-Waechter um echte Funktion.
- self-repair.sh + autoupdate.sh: bei rotem Hermes-Update zieht die Box Config-Brueche selbst
  (nur eindeutige, nicht-sicherheitsrelevante Keys; Backup -> YAML-Check -> Gehirn-Check, sonst
  zurueck). Greift es nicht, haengt eine LLM-Diagnose an die Rollback-Meldung.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-03 19:23:54 +02:00
parent 357285e798
commit 612ce127fa
7 changed files with 248 additions and 3 deletions
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
# Selbstreparatur-Modus (Autonomie 0g, Kür): Macht ein Hermes-Update den Gehirn-Check rot,
# WEIL sich Config-Schlüssel geändert haben (v0.18-Klasse: „Unknown key … → stiller Default →
# alle Tools hingen"), versucht die Box den Config-Bruch SELBST zu ziehen — bevor zurückgerollt
# wird, um die neue Version zu behalten. NUR Config, NIE Code, NIE Security-Schlüssel.
#
# Sicher durch Kapselung — jeder Schritt ist reversibel und hart gegatet:
# Backup → nur EINDEUTIGE (genau 1× vorkommende), nicht-sicherheitsrelevante Keys auskommentieren
# → YAML muss gültig bleiben → Gehirn-Check (hermes-postcheck.sh) muss GRÜN werden → sonst zurück.
# Schlimmster Fall bei Fehlgriff = exakt der heutige: Rollback. Der Aufrufer (autoupdate.sh) ruft
# dies VOR seinem Rollback; bei Rückgabe 2 rollt er wie gehabt zurück + pinnt.
#
# Rückgabe: 0 = repariert (Gehirn-Check danach grün) · 2 = nichts sicher reparierbar.
# Gibt bei Misserfolg eine Zeile „SELFREPAIR_SUGGESTION=<LLM-Diagnose>" auf stdout aus.
set -uo pipefail
SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
HERMES="${HERMES_HOME:-$HOME/.hermes}"
CFG="$HERMES/config.yaml"
WARN_FILE="${HERMES_CONFIG_WARNINGS:-/tmp/hermes-config-warnings.txt}"
LLAMA="${LLAMA_SWAP_URL:-http://127.0.0.1:8080}"
VENV_PY="$HERMES/hermes-agent/venv/bin/python"
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
say(){ echo "[self-repair] $*"; }
# ── 1) Evidenz: Postcheck-Warnungen + frisches Gateway-Journal ────────────────
EVID="$(cat "$WARN_FILE" 2>/dev/null)"
JOURN="$(journalctl --user -u hermes-gateway --since '-6 minutes' --no-pager -o cat 2>/dev/null \
| grep -iE "Unknown [a-z._]+ '|deprecated .*(setting|option|config)|defaulting to|invalid config" \
| grep -v check_fn | head -10)"
[ -z "$EVID" ] && EVID="$JOURN"
if [ -z "$EVID" ]; then
say "Keine Config-Bruch-Hinweise gefunden — hier ist (sicher) nichts zu reparieren."
exit 2
fi
say "Config-Hinweise:"; echo "$EVID"
# LLM-Diagnose/Empfehlung (best-effort) — für die Meldung, egal ob wir reparieren.
SUGG="$(curl -sf -m 60 "$LLAMA/v1/chat/completions" -H 'Content-Type: application/json' \
-d "$(python3 - "$EVID" <<'PY'
import json, sys
p = ("Ein Hermes-Agent-Update hat den Selbsttest rot gemacht. Log-Hinweise:\n" + sys.argv[1] +
"\n\nNenne auf Deutsch in 1-2 kurzen Sätzen die wahrscheinliche Ursache und die konkrete "
"Config-Korrektur in ~/.hermes/config.yaml (Schlüssel → neuer Wert). Keine Einleitung.")
print(json.dumps({"model": "coder-lite", "max_tokens": 200, "temperature": 0.1,
"messages": [{"role": "user", "content": p}]}))
PY
)" 2>/dev/null | python3 -c 'import sys,json; print((json.load(sys.stdin).get("choices") or [{}])[0].get("message",{}).get("content","").strip())' 2>/dev/null)"
# ── 2) Offending Leaf-Keys ziehen ────────────────────────────────────────────
KEYS="$(echo "$EVID" | grep -oE "Unknown [a-zA-Z0-9_.]+|'[a-zA-Z0-9_.]+'" | sed -E "s/^Unknown //; s/'//g" | sort -u)"
LEAVES="$(for k in $KEYS; do echo "${k##*.}"; done | sort -u | grep -vE '^(true|false|null|)$')"
SEC_RX='token|secret|password|approval|api_server_key|ufw|sudo|allowlist|credential'
BAK="$CFG.selfrepair-bak-$(date +%Y%m%d-%H%M%S)"
cp -a "$CFG" "$BAK"
CHANGED=""; SKIPPED=""
for leaf in $LEAVES; do
if echo "$leaf" | grep -qiE "$SEC_RX"; then SKIPPED="$SKIPPED $leaf(security-tabu)"; continue; fi
# Nur EINDEUTIGE Schlüssel-Zeilen (genau 1×) anfassen — Mehrdeutige sind zu riskant.
n="$(grep -cE "^[[:space:]]*${leaf}:" "$CFG" 2>/dev/null || echo 0)"
if [ "$n" = "1" ]; then
sed -i -E "s|^([[:space:]]*)(${leaf}:.*)$|\1# [selbstrepariert $(date +%F)] \2|" "$CFG"
CHANGED="$CHANGED $leaf"; say "auskommentiert: $leaf (unbekannt/veraltet)"
else
SKIPPED="$SKIPPED $leaf(${n}x)"
fi
done
[ -n "$SKIPPED" ] && say "nicht automatisch angefasst:$SKIPPED"
if [ -z "$CHANGED" ]; then
say "Nichts sicher automatisch reparierbar."
cp -a "$BAK" "$CFG"
echo "SELFREPAIR_SUGGESTION=$SUGG"
exit 2
fi
# ── 3) YAML noch gültig? ──────────────────────────────────────────────────────
if [ -x "$VENV_PY" ] && ! "$VENV_PY" -c "import yaml; yaml.safe_load(open('$CFG'))" 2>/dev/null; then
say "YAML nach Fix ungültig → zurück zum Backup."
cp -a "$BAK" "$CFG"; echo "SELFREPAIR_SUGGESTION=$SUGG"; exit 2
fi
# ── 4) Neustart + Gehirn-Check ────────────────────────────────────────────────
systemctl --user restart hermes-gateway; sleep 6
if bash "$SRC_DIR/hermes-postcheck.sh" >/dev/null 2>&1; then
say "Gehirn-Check nach Selbstreparatur GRÜN."
bash "$SRC_DIR/notify.sh" -s "[Box-Update]" \
"Commander, nach dem Hermes-Update hakte der Selbsttest wegen geänderter Einstellungen — ich habe das selbst gerichtet (angepasst:${CHANGED// /, }). Der Test ist wieder grün, die neue Version läuft. Ein Rollback war nicht nötig." || true
exit 0
fi
say "Auch nach Selbstreparatur rot → zurück zum Backup (der Aufrufer rollt zurück)."
cp -a "$BAK" "$CFG"
systemctl --user restart hermes-gateway; sleep 6
echo "SELFREPAIR_SUGGESTION=$SUGG"
exit 2