diff --git a/deploy/backup.sh b/deploy/backup.sh index 7d77c85..6f36b8e 100644 --- a/deploy/backup.sh +++ b/deploy/backup.sh @@ -42,6 +42,11 @@ mkdir -p "$STAGE/mem0" "$STAGE/hermes" "$STAGE/llama-swap" [ -f "$HERMES/pc-paths.yaml" ] && cp -a "$HERMES/pc-paths.yaml" "$STAGE/hermes/" || true _TOKDROPIN="$HOME/.config/systemd/user/hermes-builtin-ui.service.d/session-token.conf" [ -f "$_TOKDROPIN" ] && { mkdir -p "$STAGE/hermes/systemd-dropins"; cp -a "$_TOKDROPIN" "$STAGE/hermes/systemd-dropins/"; } || true +# Cron-Jobs + Radar-State (10.07.2026) — jobs.json liegt AUSSERHALB des Repos; ohne diese +# Zeilen verliert ein Restore ALLE Hermes-Crons (traum/Briefing/Monats-Review/Chef-Gutachter/ +# Release-Radar) still: +[ -d "$HERMES/cron" ] && cp -a "$HERMES/cron" "$STAGE/hermes/cron" || true +[ -d "$HERMES/state" ] && cp -a "$HERMES/state" "$STAGE/hermes/state" || true # --- Known-Good-Manifest ----------------------------------------------------- # Versions-Schnappschuss des LAUFENDEN Stacks: pip freeze je venv, Engine-Build, diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 2ba49ed..f620067 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -84,7 +84,10 @@ cp "$SRC/deploy/fremdblick.sh" "$HOME/.hermes/scripts/fremdblick.sh" cp "$SRC/deploy/worker.sh" "$HOME/.hermes/scripts/worker.sh" # Chef-Gutachter-Cron (Orchestrator-Finale): gpt-oss urteilt nachts über die autonome Arbeit. cp "$SRC/deploy/chef-gutachter-feed.sh" "$HOME/.hermes/scripts/chef-gutachter-feed.sh" -chmod +x "$HOME/.hermes/scripts/fremdblick.sh" "$HOME/.hermes/scripts/dreaming-feed.sh" "$HOME/.hermes/scripts/worker.sh" "$HOME/.hermes/scripts/chef-gutachter-feed.sh" +# Release-Radar-Cron (10.07.2026): hält neue hermes-agent-Releases wöchentlich gegen die +# Eigenbau-Landkarte im Wissens-Vault ("mehr Hermes nativ, weniger Eigenkreationen"). +cp "$SRC/deploy/hermes-release-radar-feed.sh" "$HOME/.hermes/scripts/hermes-release-radar-feed.sh" +chmod +x "$HOME/.hermes/scripts/fremdblick.sh" "$HOME/.hermes/scripts/dreaming-feed.sh" "$HOME/.hermes/scripts/worker.sh" "$HOME/.hermes/scripts/chef-gutachter-feed.sh" "$HOME/.hermes/scripts/hermes-release-radar-feed.sh" # Werkstatt-Skill (Autonomie E6): Selbstwartungs-Kreislauf für Hermes. mkdir -p "$HOME/.hermes/skills/wartung" cp "$SRC/deploy/skills/wartung/SKILL.md" "$HOME/.hermes/skills/wartung/SKILL.md" diff --git a/deploy/hermes-release-radar-feed.sh b/deploy/hermes-release-radar-feed.sh new file mode 100644 index 0000000..a440396 --- /dev/null +++ b/deploy/hermes-release-radar-feed.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash +# Hermes-Release-Radar (woechentlich, Mo 05:15) — "mehr Hermes nativ, weniger Eigenkreationen": +# haelt neue hermes-agent-Releases gegen die Eigenbau-Landkarte im Wissens-Vault und meldet +# NUR echte Treffer auf Abloese-Kriterien/Wiedervorlage-Bedingungen. Muster wie +# chef-gutachter-feed.sh: liegt in ~/.hermes/scripts/ (deploy.sh kopiert ihn), stdout wird +# dem Cron-Agenten als Prompt eingespeist. Der ABGLEICH laeuft als Ein-Schuss-Completion +# direkt gegen llama-swap (:8080, fremdblick-Architektur): Analyst gpt-oss-120b (um 05:15 +# vom Chef-Gutachter 04:30 oft noch geladen), Vertretung GLM-4.7-Flash. Danach gatet +# fremdblick.sh (Prosa-Raster, Fremd-Vendor) jede Treffer-Behauptung gegen die +# Original-Changelogs. +# Grundsaetze (stehen auch in der Landkarte): nur getaggte Releases (kein main-Churn bei +# ~660 PRs/Woche) · Behauptung nur mit woertlichem Changelog-Zitat · Treffer => Probe- +# Vorschlag, NIE Direkt-Adoption · der Radar informiert, er updatet nicht (Entscheid 04.07.). +# State: ~/.hermes/state/release-radar-last.txt (zuletzt gemeldeter Tag). Verlust harmlos — +# ohne State dient die installierte Version als Basis. +set -uo pipefail + +MC="$HOME/mission-control-v2" +LANDKARTE="$HOME/wissens-vault/eigenbau-landkarte.md" +STATE_DIR="$HOME/.hermes/state" +STATE="$STATE_DIR/release-radar-last.txt" +API="${RADAR_RELEASES_URL:-https://api.github.com/repos/NousResearch/hermes-agent/releases?per_page=10}" +ENDPOINT="${RADAR_ENDPOINT:-http://127.0.0.1:8080/v1/chat/completions}" +ANALYST="${RADAR_MODEL:-gpt-oss-120b}" +VERTRETUNG="${RADAR_FALLBACK:-GLM-4.7-Flash}" +FREMDBLICK="$HOME/.hermes/scripts/fremdblick.sh" + +mkdir -p "$STATE_DIR" + +# Briefkasten-Spiegel (still, wie die Morgenlage): Cockpit/Chronik lesen source=release-radar. +briefkasten() { # $1=Betreff $2=Text + curl -sf -m 5 -X POST "${MC_ANNOUNCE_URL:-http://127.0.0.1:9001/api/voice/announce}" \ + -H 'Content-Type: application/json' \ + --data "$(python3 - "$1" "$2" <<'PY' +import json, sys +print(json.dumps({"text": sys.argv[2], "subject": sys.argv[1], + "source": "release-radar", "priority": "silent"})) +PY +)" >/dev/null 2>&1 || true +} + +# ---------- Versionierter Auftrag fuer den Boten-Agenten ---------- +cat "$MC/deploy/release-radar-prompt.md" 2>/dev/null || cat <<'EOF' +(Prompt-Datei fehlt noch — Kurzform:) Du bist der Bote des woechentlichen Release-Radars. +Gib den Befund unten in Lucys Stimme wieder: bei "KEIN NEUES RELEASE" oder "TREFFER: 0" +genau EIN kurzer Satz; bei Treffern maximal 5 Saetze. Nichts installieren oder aendern. +EOF +echo + +INSTALLED_LINE="$(bash -lc 'hermes --version' 2>/dev/null | head -1)" +INSTALLED_TAG="$(printf '%s' "$INSTALLED_LINE" | grep -oE '\([0-9][0-9.]*\)' | head -1 | tr -d '()')" +[ -n "$INSTALLED_TAG" ] && INSTALLED_TAG="v$INSTALLED_TAG" +LAST_SEEN="$(cat "$STATE" 2>/dev/null | tr -d '[:space:]' || true)" +BASELINE="${LAST_SEEN:-$INSTALLED_TAG}" + +RELEASES_JSON="$(curl -sf --max-time 30 "$API" 2>/dev/null || true)" +if [ -z "$RELEASES_JSON" ]; then + echo "## RADAR-BEFUND: AUSGEFALLEN" + echo "GitHub-Releases-API nicht erreichbar. Dem Commander EINEN ehrlichen Satz melden; naechste Woche neuer Versuch." + exit 0 +fi + +# Neue getaggte Releases seit BASELINE (Drafts/Prereleases raus, max. 6, Bodies gekuerzt). +# Baseline unbekannt (aelter als die letzten 10 oder Erstlauf ohne Version) -> juengste 6. +PARSED="$(printf '%s' "$RELEASES_JSON" | python3 - "$BASELINE" <<'PY' +import json, sys +baseline = sys.argv[1] +try: + rels = [r for r in json.load(sys.stdin) if not r.get("draft") and not r.get("prerelease")] +except Exception: + rels = [] +rels.sort(key=lambda r: r.get("published_at") or "", reverse=True) +base = next((r.get("published_at") for r in rels if r.get("tag_name") == baseline), None) +new = rels[:6] if base is None else [r for r in rels if (r.get("published_at") or "") > base][:6] +print((new or rels or [{"tag_name": baseline}])[0].get("tag_name") or baseline) +print(len(new)) +for r in new: + body = (r.get("body") or "").strip() + if len(body) > 3500: + body = body[:3500] + "\n[... gekuerzt ...]" + print(f"\n===== RELEASE {r.get('tag_name')} — {(r.get('name') or '').strip()} ({(r.get('published_at') or '')[:10]}) =====") + print(body) +PY +)" +NEWEST="$(printf '%s\n' "$PARSED" | sed -n 1p)" +COUNT="$(printf '%s\n' "$PARSED" | sed -n 2p)" +NOTES="$(printf '%s\n' "$PARSED" | tail -n +3)" + +echo "## RADAR-BEFUND (erhoben am $(date '+%d.%m.%Y %H:%M'))" +echo "Box installiert: ${INSTALLED_LINE:-unbekannt}" +echo "Zuletzt gemeldet: ${BASELINE:-unbekannt} · neuestes Release: $NEWEST · neue Releases: ${COUNT:-0}" +echo "(Updates bleiben manuell/bestaetigt — der Radar informiert nur.)" +echo + +if [ "${COUNT:-0}" = "0" ]; then + echo "KEIN NEUES RELEASE seit $BASELINE." + [ -n "$NEWEST" ] && printf '%s\n' "$NEWEST" > "$STATE" + briefkasten "Release-Radar" "Kein neues hermes-agent-Release seit $BASELINE (Box: ${INSTALLED_TAG:-?})." + exit 0 +fi + +if [ ! -s "$LANDKARTE" ]; then + echo "### ABGLEICH NICHT MOEGLICH" + echo "$COUNT neue Releases, aber die Eigenbau-Landkarte fehlt ($LANDKARTE)." + echo "Dem Commander melden: Landkarte im Wissens-Vault wiederherstellen. State bleibt unveraendert (naechste Woche neuer Versuch)." + briefkasten "Release-Radar" "$COUNT neue hermes-agent-Releases, aber die Eigenbau-Landkarte fehlt — Abgleich uebersprungen." + exit 0 +fi + +RASTER='Du bist der RELEASE-RADAR einer lokalen KI-Box. Du bekommst (1) die EIGENBAU-LANDKARTE (selbstgebaute Bausteine mit Abloese-Kriterien und Wiedervorlage-Bedingungen) und (2) CHANGELOGS neuer hermes-agent-Releases. Pruefe AUSSCHLIESSLICH: Trifft eine Changelog-Zeile ein Abloese-Kriterium oder eine Wiedervorlage-Bedingung der Landkarte? REGELN: Jede Treffer-Behauptung MUSS die Changelog-Zeile WOERTLICH zitieren. Marketing-Prosa ohne konkretes Feature ist KEIN Treffer. Vage Aehnlichkeit ist KEIN Treffer. Eintraege unter "Bewusst NICHT adoptiert" nur melden, wenn exakt die Wiedervorlage-Bedingung erfuellt ist. Erfinde nichts. Antworte auf DEUTSCH, exakt in dieser Struktur: erste Zeile "TREFFER: ". Danach je Treffer ein Block: "EIGENBAU: " / "RELEASE: " / "ZITAT: " / "WARUM: " / "PROBE: ". Bei TREFFER: 0 stattdessen EIN Satz, was die Releases fuer uns Unwichtiges bringen.' + +EVID="EIGENBAU-LANDKARTE: +$(cat "$LANDKARTE") + +CHANGELOGS DER NEUEN RELEASES: +$NOTES" + +# Ein-Schuss-Analyst: $1=Modell $2=curl-Timeout $3=max_tokens (Muster chef-gutachter-feed.sh; +# max_tokens grosszuegig, weil Reasoning-Modelle Budget im reasoning_content verbrauchen). +judge() { + local req + req="$(jq -n --arg m "$1" --arg sys "$RASTER" --arg usr "$EVID" --argjson mt "$3" \ + '{model:$m, messages:[{role:"system",content:$sys},{role:"user",content:$usr}], + max_tokens:$mt, temperature:0.2}')" + curl -s -m "$2" "$ENDPOINT" -H 'Content-Type: application/json' --data-binary "$req" \ + | python3 -c ' +import json, sys +try: + d = json.load(sys.stdin) + msg = d["choices"][0]["message"] + print((msg.get("content") or msg.get("reasoning_content") or "").strip()) +except Exception: + pass' +} + +RICHTER="$ANALYST" +ANALYSE="$(judge "$ANALYST" 420 2600)" +if [ -z "${ANALYSE// /}" ]; then + RICHTER="$VERTRETUNG (Vertretung — $ANALYST hat nicht geantwortet)" + ANALYSE="$(judge "$VERTRETUNG" 240 1800)" +fi + +if [ -z "${ANALYSE// /}" ]; then + echo "### ABGLEICH AUSGEFALLEN" + echo "Beide Analysten ($ANALYST, $VERTRETUNG) haben nicht geantwortet. State bleibt unveraendert — naechste Woche neuer Versuch. Dem Commander EINEN ehrlichen Satz melden." + briefkasten "Release-Radar" "$COUNT neue hermes-agent-Releases, aber der Abgleich fiel aus (kein Analyst erreichbar)." + exit 0 +fi + +echo "### Neue Releases:" +printf '%s\n' "$NOTES" | grep '^===== RELEASE' | sed 's/^===== //; s/ =====$//' +echo +echo "### ABGLEICH gegen die Eigenbau-Landkarte (Analyst: $RICHTER)" +echo "$ANALYSE" +echo + +# Fremdblick-Gate nur bei Treffern (Zitate MIT Quellmaterial gegenpruefbar machen). +TREFFER="$(printf '%s' "$ANALYSE" | grep -oE 'TREFFER: *[0-9]+' | head -1 | grep -oE '[0-9]+' || true)" +GEGEN="" +if [ "${TREFFER:-1}" != "0" ] && [ -x "$FREMDBLICK" ]; then + GEGEN="$(printf '%s\n\nQUELLMATERIAL (Original-Changelogs zum Gegenpruefen der Zitate):\n%s' "$ANALYSE" "$NOTES" | "$FREMDBLICK")" + echo "$GEGEN" + echo +fi + +printf '%s\n' "$NEWEST" > "$STATE" +briefkasten "Release-Radar" "Neue hermes-agent-Releases bis $NEWEST (Box: ${INSTALLED_TAG:-?}). + +ABGLEICH (Analyst: $RICHTER): +$ANALYSE + +${GEGEN:-(Kein Fremdblick noetig: TREFFER 0)}" diff --git a/deploy/release-radar-prompt.md b/deploy/release-radar-prompt.md new file mode 100644 index 0000000..2ac4f06 --- /dev/null +++ b/deploy/release-radar-prompt.md @@ -0,0 +1,25 @@ +# Release-Radar — Auftrag für den Boten-Agenten + +Du bist der Bote des wöchentlichen Hermes-Release-Radars. Unten stehen ein RADAR-BEFUND +(automatisch erhoben), bei neuen Releases ein ABGLEICH eines Analysten-Modells gegen die +Eigenbau-Landkarte (`~/wissens-vault/eigenbau-landkarte.md`) und ein FREMDBLICK +(Zweitmeinung eines anderen Modells, prüft die Changelog-Zitate). + +Deine Aufgabe — NUR berichten, in Lucys Stimme (Anrede „Commander", Alltagssprache, +Fazit zuerst): + +1. Bei „KEIN NEUES RELEASE" oder „TREFFER: 0": antworte mit GENAU EINEM kurzen Satz + (z. B. „Radar: hermes vX ist da, nichts für unsere Landkarte dabei."). +2. Bei Treffern: maximal 5 Sätze — welcher Eigenbau betroffen ist, welches native Feature + das Release bringt, der Kern des Zitats, die empfohlene Probe, und was der Fremdblick + dazu sagt (TRAEGT / TRAEGT-NICHT ehrlich nennen, nichts glattbügeln). +3. Bei „AUSGEFALLEN" oder „NICHT MOEGLICH": ein ehrlicher Satz, dass der Radar diese + Woche blind war und warum. + +Harte Regeln: +- NICHTS installieren, updaten, mergen oder konfigurieren — der Radar informiert nur. + Updates bleiben eine manuelle Entscheidung des Commanders (Entscheid 04.07.). +- Erfinde keine Treffer über den ABGLEICH hinaus; vom Fremdblick abgelehnte Behauptungen + (TRAEGT-NICHT) als abgelehnt benennen, nicht verschweigen. +- Die stille Briefkasten-Karte hat das Skript bereits geschrieben — du lieferst nur die + kurze Telegram-Meldung. diff --git a/deploy/restore.sh b/deploy/restore.sh index 29d3ab0..7a839a5 100644 --- a/deploy/restore.sh +++ b/deploy/restore.sh @@ -130,6 +130,10 @@ if [ -d "$STAGE/mem0" ]; then rm -rf "$MEM0_DIR"; mkdir -p "$MEM0_DIR"; cp -a "$ [ -f "$STAGE/hermes/desktop-gateway-token" ] && { cp -a "$STAGE/hermes/desktop-gateway-token" "$HERMES/"; chmod 600 "$HERMES/desktop-gateway-token"; } [ -f "$STAGE/hermes/pc-paths.yaml" ] && cp -a "$STAGE/hermes/pc-paths.yaml" "$HERMES/" [ -f "$STAGE/hermes/systemd-dropins/session-token.conf" ] && { mkdir -p "$HOME/.config/systemd/user/hermes-builtin-ui.service.d"; cp -a "$STAGE/hermes/systemd-dropins/session-token.conf" "$HOME/.config/systemd/user/hermes-builtin-ui.service.d/"; chmod 600 "$HOME/.config/systemd/user/hermes-builtin-ui.service.d/session-token.conf"; systemctl --user daemon-reload 2>/dev/null || true; } +# Cron-Jobs + Radar-State (Gegenstück zu backup.sh, 10.07.2026) — der Gateway-Neustart unten +# lädt die wiederhergestellten Jobs: +[ -d "$STAGE/hermes/cron" ] && { mkdir -p "$HERMES/cron"; cp -a "$STAGE/hermes/cron/." "$HERMES/cron/"; } +[ -d "$STAGE/hermes/state" ] && { mkdir -p "$HERMES/state"; cp -a "$STAGE/hermes/state/." "$HERMES/state/"; } echo "→ Dienste starten…" systemctl --user start $SERVICES 2>/dev/null || true diff --git a/deploy/skills/orchestrator/SKILL.md b/deploy/skills/orchestrator/SKILL.md index b56ae70..4e8541b 100644 --- a/deploy/skills/orchestrator/SKILL.md +++ b/deploy/skills/orchestrator/SKILL.md @@ -62,6 +62,10 @@ dann kleiner. 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. +- **Hermes-first (10.07.2026):** Bevor ein Teil-Task NEUE Infrastruktur baut (Skript, Dienst, + Tool, Cron), pruefe ob hermes-agent das nativ kann — erst in die Eigenbau-Landkarte schauen + (`~/wissens-vault/eigenbau-landkarte.md`), notfalls `bash -lc 'hermes --help'`. Den + Pruef-Befund in den Vorschlag schreiben („nativ vorhanden: nein/ja, weil …"). - Gate rot oder Kritiker dagegen → trotzdem ehrlich melden (Branch bleibt liegen), nichts beschoenigen. ## Ablauf diff --git a/deploy/skills/wartung/SKILL.md b/deploy/skills/wartung/SKILL.md index 3f05b6a..9cebc49 100644 --- a/deploy/skills/wartung/SKILL.md +++ b/deploy/skills/wartung/SKILL.md @@ -22,6 +22,11 @@ NUR einen Plan liefern (Text im Telegram-Vorschlag), KEINEN Code. - NIEMALS auf `main` committen. NIEMALS mergen. NIEMALS deployen oder Dienste neu starten. - Security-Config ist TABU: keine Tokens, approvals, ufw, sudoers anfassen. +- **Hermes-first (10.07.2026):** Bevor du NEUE Infrastruktur baust oder vorschlägst (Skript, + Dienst, Tool, Cron), prüfe ob hermes-agent das nativ kann — erst in die Eigenbau-Landkarte + schauen (`~/wissens-vault/eigenbau-landkarte.md`: Eigenbauten ↔ nativ, Verdikte), notfalls + `bash -lc 'hermes --help'`. Den Prüf-Befund in den Vorschlag schreiben („nativ vorhanden: + nein/ja, weil …"). Weniger Eigenkreationen = weniger Wartungsberg. - Die Live-Instanz (`~/mission-control-v2`) bleibt unberührt — gearbeitet wird NUR im Worktree. - Am Ende steht IMMER ein Telegram-Vorschlag; die Entscheidung trifft der User. - Gate rot oder Reviewer dagegen → trotzdem ehrlich melden (Branch bleibt liegen), nichts beschönigen.