#!/bin/bash # SubConfirm — Proaktiver Stasis-Detektor für Subagenten # # Läuft als Hintergrund-Daemon und prüft alle 30 Sekunden alle tmux-Sessions. # Wenn eine Session ihren Output seit >30s nicht verändert hat (Stasis), # wird der vollständige Pane-Inhalt in die Alert-Datei geschrieben. # Die arbeitsweise-guard.ts Extension zeigt diesen Alert dem Orchestrator # beim nächsten Tool-Call — der Orchestrator beurteilt dann selbst ob # Handlung nötig ist. # # KEIN Keyword-Matching — der Orchestrator entscheidet was zu tun ist. # # Architektur: # SubConfirm → /tmp/.pi-subagent-alert → arbeitsweise-guard.ts → Orchestrator # # Nutzung: # SubConfirm & # Im Hintergrund starten # SubConfirm --interval 15 & # Kürzeres Intervall # SubConfirm --skip "main-session" & # Session ausschließen (Orchestrator-Session) # pkill -f SubConfirm # Beenden # # Autostart: In AGENTS.md Session-Start-Checkliste eingetragen. set -euo pipefail INTERVAL=30 SKIP_SESSION="" REPORT_COOLDOWN=90 # Sekunden zwischen wiederholten Meldungen zur selben Session ALERT_FILE="/tmp/.pi-subagent-alert" STATE_DIR="/tmp/.pi-subconfirm-state" PID_FILE="/tmp/.pi-subconfirm.pid" while [[ $# -gt 0 ]]; do case "$1" in --interval) INTERVAL="$2"; shift 2 ;; --skip) SKIP_SESSION="$2"; shift 2 ;; *) shift ;; esac done mkdir -p "$STATE_DIR" echo $$ > "$PID_FILE" log() { echo "[SubConfirm $(date '+%H:%M:%S')] $1" >&2 } alert() { local msg="$1" # In Alert-Datei schreiben (Guard zeigt das beim nächsten Tool-Call) echo "$(date '+%H:%M:%S') $msg" >> "$ALERT_FILE" # Desktop-Notification als zusätzlicher Hinweis zenity --notification --text="SubConfirm: $msg" 2>/dev/null & echo -e "\a" 2>/dev/null || true } log "Gestartet (PID $$, Intervall: ${INTERVAL}s, Skip: '${SKIP_SESSION}')" while true; do sleep "$INTERVAL" SESSIONS=$(tmux ls -F '#{session_name}' 2>/dev/null || echo "") [ -z "$SESSIONS" ] && continue while IFS= read -r session; do [ -z "$session" ] && continue [ "$session" = "$SKIP_SESSION" ] && continue # Pane-Inhalt holen (letzte 25 Zeilen, ANSI-Escape-Codes entfernen) CURRENT=$(tmux capture-pane -t "$session" -p 2>/dev/null \ | sed 's/\x1b\[[0-9;]*[mGKHF]//g' \ | grep -v '^[[:space:]]*$' \ | tail -25 \ | tr '\n' '§') [ -z "$CURRENT" ] && continue STATE_FILE="${STATE_DIR}/${session//\//_}.state" REPORT_FILE="${STATE_DIR}/${session//\//_}.reported" PREV=$(cat "$STATE_FILE" 2>/dev/null || echo "") if [ "$CURRENT" = "$PREV" ]; then # Stasis — Output hat sich nicht verändert seit letztem Check NOW=$(date +%s) LAST_REPORT=$(cat "$REPORT_FILE" 2>/dev/null || echo 0) if [ $((NOW - LAST_REPORT)) -gt "$REPORT_COOLDOWN" ]; then PANE_DISPLAY=$(echo "$CURRENT" | tr '§' '\n') alert "⏸️ SUBAGENT-STASIS [$session] — Output seit >${INTERVAL}s unverändert. Pane-Inhalt: $PANE_DISPLAY Mögliche Reaktionen: Bestätigen (Yes): tmux send-keys -t \"$session\" \"\" Enter Ablehnen (No): tmux send-keys -t \"$session\" \"\" && tmux send-keys -t \"$session\" \"\" Enter Ignorieren: (keine Aktion — nächste Meldung in ${REPORT_COOLDOWN}s wenn noch Stasis)" log "Stasis gemeldet: $session" echo "$NOW" > "$REPORT_FILE" fi else # Aktivität — State aktualisieren, Stasis-Zustand zurücksetzen echo "$CURRENT" > "$STATE_FILE" rm -f "$REPORT_FILE" fi done <<< "$SESSIONS" done