108 lines
3.7 KiB
Text
108 lines
3.7 KiB
Text
|
|
#!/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
|