Welle 3 · Lucys Freiheiten je Aktionsart, Rückfragen mit „ja 7“, Lucy untersucht rote Hinweise, Wochenbericht

- services/lucy_autonomie.py: Stufe je Aktionsart (nur lesen, erst fragen, selbst handeln), Start „mehr Freiheit“;
  Meldung danach (POST /api/lucy/gehandelt)
- services/rueckfragen.py: nummerierte Rückfragen, nur Knöpfe der Oberfläche (ERLAUBT), „ja“ führt genau die
  gespeicherte Aktion aus, 24 h gültig, hinfällig wenn der Hinweis weg ist, Nachprüfung danach
- notify.sh -k: Antworttasten (Antworttastatur über den Direktweg, ohne Eingriff in Hermes)
- services/ermittlung.py: Wächter meldet rote Hinweise an Lucy; Knopf wird Rückfrage, Lucy sieht über den
  api_server nach und schickt Ursache und Weg als eine Nachricht
- services/wochenbericht.py + mc2-wochenbericht.timer (So 07:30): feste Sätze, Absatz vom Modell, offene
  App-Updates als Rückfragen
- MCP: Werkzeuge fragen die Freiheiten (auf_bitte), neu: freiheiten, rueckfragen, rueckfrage_beantworten,
  sicherheitsupdates_einspielen, snapshot_zurueckspielen, snapshots, geraet_logs, dienst_logs
- Ausführer-Aktion journal (nur lesend), POST /api/homelab/ziele/{id}/sicherheitsupdates
- Läufe des Homelabs mit flock (Oberfläche und Steward schreiben beide)
- Skill homelab-orchestrator, SOUL-Abschnitt als Kopie in deploy/nur-box

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-25 19:12:50 +02:00
co-authored by Claude Opus 5.5
parent dd23f3fb4c
commit 12cefb427a
37 changed files with 2649 additions and 169 deletions
+31 -4
View File
@@ -51,7 +51,7 @@ from kern import partner, prognose
from kern.einstellungen import einstellungen
from kern.zeit import LOCAL_TZ
from services import announce, llamaswap, maintenance, probe_wiederherstellung, update_verlauf
from services import announce, llamaswap, maintenance, probe_wiederherstellung, rueckfragen, update_verlauf
log = logging.getLogger(__name__)
ROLLE = einstellungen().rolle
@@ -83,6 +83,9 @@ VERLAUF_MAX = 200
# Trockenlauf (Probelauf neben dem echten Betrieb): prüft und führt Hinweise, meldet aber
# nichts an Telegram/Lucy und repariert nichts selbst — das macht weiter der echte Wächter.
TROCKEN = os.environ.get("MC_WAECHTER_TROCKEN", "") == "1"
# Rote Hinweise an Lucy auf der KI-Box (seit 25.09.2026, _an_lucy). Die Tests schalten das ab (conftest.py) — auf der
# Box liefe es sonst beim Prüftor gegen das echte MC2.
LUCY_BEFUND = os.environ.get("MC_LUCY_BEFUND", "1") != "0"
# Im Steward-Modus beobachtet er auch MC2 selbst und den Gateway (wie der alte Wächter).
WATCH_MC2 = os.environ.get("MC_SENTRY_WATCH_MC2", os.environ.get("MC_WAECHTER_WATCH_MC2", "")) == "1"
@@ -112,6 +115,8 @@ TIMER_DIENSTE: dict[str, tuple[str, bool]] = {
# Seit 24.09.2026 im Repo (vom 21.08. an aus, weil sie rot lief und niemand es sah). Gelb: Die tägliche
# Sicherung (mc2-backup) bleibt die erste Schicht, der PBS ist die Kopie auf dem QNAP. Ausgeschaltet kein Befund.
"pbs-backup": ("Sicherung auf den PBS", False),
# Seit 25.09.2026 (Ausbauplan Welle 3): sonntags 07:30 der Wochenbericht in Lucys Stimme.
"mc2-wochenbericht": ("Wochenbericht", False),
}
HERMES_JOBS_PATH = HERMES_HOME / "cron" / "jobs.json"
@@ -674,14 +679,34 @@ def _verlauf(art: str, hinweis_id: str, text: str, stufe: str | None = None, que
del _stand["verlauf"][:-VERLAUF_MAX]
def _telegram(betreff: str, text: str) -> None:
def _telegram(betreff: str, text: str, knoepfe: list[str] | None = None) -> None:
if TROCKEN:
log.info("waechter (trocken): würde melden %s %s", betreff, text)
return
announce.add(text, subject=betreff, source="waechter")
# Bis 25.09.2026 hing hier „(Diese Meldung kam auch an Lucy.)“ an — als wäre Lucy jemand anderes als der Absender.
# Sie IST der Absender (ihr Bot, ihre Stimme, siehe notify.sh); im Briefkasten oben liegt die Meldung trotzdem.
announce.notify_telegram(betreff, text)
announce.notify_telegram(betreff, text, knoepfe=knoepfe)
def _an_lucy(b: Befund, erinnerung: bool) -> tuple[list[str], list[str]]:
"""Roter Hinweis (seit 25.09.2026, Ausbauplan Welle 3): Auf der KI-Box entscheidet Lucy, ob sie nachsieht
(services/ermittlung.py), und der Knopf des Hinweises wird zur Rückfrage. Zurück kommen die Zeilen für diese
Nachricht („Antworte „ja 7“, dann …“) und die Antworttasten. Ist die Box nicht erreichbar, bleibt es wie bisher."""
if TROCKEN or not LUCY_BEFUND:
return [], []
basis = rueckfragen.box_url()
if not basis:
return [], []
try:
antwort = httpx.post(f"{basis}/api/lucy/befund", timeout=8, json={
"bereich": ROLLE, "id": b.id, "titel": b.titel, "text": b.text, "stufe": b.stufe, "quelle": b.quelle,
"aktionen": b.aktionen, "erinnerung": erinnerung})
daten = antwort.json() if antwort.status_code == 200 else {}
except (httpx.HTTPError, ValueError):
return [], []
daten = daten if isinstance(daten, dict) else {}
return [str(z) for z in daten.get("zeilen") or []][:3], [str(k) for k in daten.get("knoepfe") or []][:6]
def _auto_erlaubt(schluessel: str, jetzt: float) -> bool:
@@ -750,7 +775,9 @@ def takt() -> None:
if b.stufe == "rot" and (neu or jetzt - h.get("gemeldet", 0.0) >= REMIND_S) \
and not ist_stumm(zustaende.get(b.id), b.stufe, jetzt):
vorsatz = "" if neu else "Immer noch: "
_telegram(BETREFF_PROBLEM, f"{vorsatz}{b.titel}. {b.text}")
zeilen, knoepfe = _an_lucy(b, erinnerung=not neu)
_telegram(BETREFF_PROBLEM, f"{vorsatz}{b.titel}. {b.text}" + "".join(f"\n{z}" for z in zeilen),
knoepfe or None)
h["gemeldet"] = jetzt
# Nicht mehr festgestellt → erledigt. Während eines Updates bleiben Dienst- und