Enthält alle Pi-Orchestrator-Infrastrukturkomponenten: - bin/Sub* Skripte (SubAgenten, SubStatus, SubWatcher, SubConfirm) - extensions/ (arbeitsweise-guard, confirm-deletion, etc.) - memory/ (arbeitsweise, subagent-autocheck) - agent/AGENTS.md mit SubConfirm-Reaktionslogik - install.sh: deterministisches, idempotentes Setup für neue Maschinen SubConfirm (neu): Stasis-Detektor der alle 30s tmux-Sessions prüft. Bei unverändertem Output sendet er den vollständigen Pane-Inhalt an die Alert-Datei — der Orchestrator beurteilt selbst ob Handlung nötig. Kein Keyword-Matching.
201 lines
No EOL
7.6 KiB
Bash
201 lines
No EOL
7.6 KiB
Bash
#!/bin/bash
|
||
# SubAgenten — Starte einen Subagenten mit Pi in einem isolierten Verzeichnis
|
||
#
|
||
# Nutzung: SubAgenten <aufgaben-name> <aufgaben-beschreibung> [MODELL_CMD]
|
||
#
|
||
# MODELL_CMD (optional): Welches Pi-Kommando starten (Default: GlmPi)
|
||
# Verfügbare Kommandos: TurboPi, MiniPi, GlmPi, DeepPi, FlashPi, GPTPi, GeminiPi
|
||
#
|
||
# Beispiel:
|
||
# SubAgenten "pi-sticks" "Pi auf stick2-5 installieren laut Runbook"
|
||
# SubAgenten "debug" "Debug Auth-Problem" TurboPi
|
||
# SubAgenten "quick-task" "Kurze Aufgabe" MiniPi
|
||
#
|
||
# Funktionsweise:
|
||
# 1. Isoliertes Verzeichnis in ~/.pi/subagents/<name>/ anlegen (persistent, kein /tmp)
|
||
# 2. tmux-Session starten (unsichtbar), Pi darin ausführen
|
||
# 3. Warten bis Pi bereit ist, dann /name + Aufgabe per tmux send-keys senden
|
||
# 4. gnome-terminal-Fenster öffnen und an tmux-Session attach-en
|
||
# 5. Du siehst das Fenster, Pi arbeitet — du kannst jederzeit reinschreiben
|
||
#
|
||
# Bei Rückfragen des Subagenten:
|
||
# - Das Fenster ist direkt sichtbar — einfach Antwort eintippen
|
||
# - Oder: tmux attach -t <session-name> (z.B. in einem zweiten Terminal)
|
||
# - Verlassen ohne Session zu beenden: Ctrl+B, dann D
|
||
#
|
||
# Umgebung:
|
||
# SUBAGENT_SILENT=1 — Keine Ausgabe, nur tmux-Session-Name auf stdout
|
||
|
||
set -euo pipefail
|
||
|
||
NAME="${1:-}"
|
||
TASK="${2:-}"
|
||
MODEL_CMD="${3:-GlmPi}" # Default: GlmPi (GLM 5.1)
|
||
|
||
|
||
if [ -z "$NAME" ] || [ -z "$TASK" ]; then
|
||
echo "Fehler: Name und Aufgabe erforderlich."
|
||
echo ""
|
||
echo "Nutzung: SubAgenten <aufgaben-name> <aufgaben-beschreibung> [MODELL_CMD]"
|
||
echo ""
|
||
echo "MODELL_CMD (optional): TurboPi, MiniPi, GlmPi, DeepPi, FlashPi, GPTPi, GeminiPi (Default: GlmPi)"
|
||
echo ""
|
||
echo "Beispiele:"
|
||
echo ' SubAgenten "pi-sticks" "Pi auf stick2-5 installieren"'
|
||
echo ' SubAgenten "debug" "Debug Auth-Problem" TurboPi'
|
||
exit 1
|
||
fi
|
||
|
||
# Prüfe ob das angegebene Kommando existiert
|
||
if ! command -v "$MODEL_CMD" &> /dev/null; then
|
||
echo "Warnung: '$MODEL_CMD' nicht gefunden. Verwende GlmPi als Fallback."
|
||
MODEL_CMD="GlmPi"
|
||
fi
|
||
|
||
# Slug aus dem Namen für tmux-Session + Arbeitsverzeichnis
|
||
SESSION_SLUG=$(echo "$NAME" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-')
|
||
WORKDIR="${SUBAGENT_BASE_DIR:-$HOME/.pi/subagents}/${SESSION_SLUG}"
|
||
|
||
# Prüfen ob Session bereits existiert
|
||
if tmux has-session -t "$SESSION_SLUG" 2>/dev/null; then
|
||
echo "Fehler: tmux-Session '$SESSION_SLUG' existiert bereits."
|
||
echo " Zum Ansehen: tmux attach -t $SESSION_SLUG"
|
||
echo " Zum Beenden: tmux kill-session -t $SESSION_SLUG"
|
||
exit 1
|
||
fi
|
||
|
||
# Isoliertes Verzeichnis anlegen
|
||
mkdir -p "$WORKDIR"
|
||
|
||
if [ -z "${SUBAGENT_SILENT:-}" ]; then
|
||
echo ""
|
||
echo "╔══════════════════════════════════════════╗"
|
||
echo "║ 🚀 Subagent: $NAME"
|
||
echo "╚══════════════════════════════════════════╝"
|
||
echo ""
|
||
echo "📁 Arbeitsverzeichnis: $WORKDIR"
|
||
echo "🪟 Terminal-Fenster wird geöffnet..."
|
||
echo ""
|
||
fi
|
||
|
||
# tmux-Session im Hintergrund starten (ohne Fenster)
|
||
# PI_ORCHESTRATOR explizit entfernen — darf nicht an Subagenten vererbt werden,
|
||
# sonst blockiert der arbeitsweise-guard auch im Subagenten curl/wget/write.
|
||
# MODEL_CMD ist das Pi-Kommando (z.B. GlmPi, TurboPi, MiniPi)
|
||
tmux new-session -d -s "$SESSION_SLUG" -c "$WORKDIR" "unset PI_ORCHESTRATOR; ${MODEL_CMD}; echo '=== Pi beendet ==='; read"
|
||
|
||
if [ -z "${SUBAGENT_SILENT:-}" ]; then
|
||
echo "⏳ Warte auf Pi-Start..."
|
||
fi
|
||
|
||
# Warten bis Pi seinen Prompt anzeigt (max 30 Sekunden)
|
||
# Früherer Bugfix: Pattern auf Box-Drawing-Zeichen (╭─ ───) matchte nicht,
|
||
# weil Unicode-Zeichen in tmux-Capture anders kodiert sind.
|
||
# Stattdessen: Warten auf Pi-Prompt-Indikator (auto) oder Provider-Name
|
||
TIMEOUT=30
|
||
ELAPSED=0
|
||
|
||
while [ $ELAPSED -lt $TIMEOUT ]; do
|
||
# Prüfen ob tmux-Session noch lebt
|
||
if ! tmux has-session -t "$SESSION_SLUG" 2>/dev/null; then
|
||
echo "Fehler: tmux-Session '$SESSION_SLUG' ist abgestürzt."
|
||
exit 1
|
||
fi
|
||
|
||
# Prüfen ob Pi bereit ist — suche nach Prompt-Indikatoren:
|
||
# - (auto) = auto-modus aktiv
|
||
# - Provider-Kürzel wie (zai), (openrouter)
|
||
# - Arbeitsverzeichnis im Prompt
|
||
# -a = treat binary as text (tmux-capture liefert UTF-8 Bytes)
|
||
CAPTURE=$(tmux capture-pane -t "$SESSION_SLUG" -p 2>/dev/null | tail -3)
|
||
if echo "$CAPTURE" | grep -a -qE "\(auto\)|\(zai\)|\(openrouter\)" 2>/dev/null; then
|
||
break
|
||
fi
|
||
|
||
sleep 1
|
||
ELAPSED=$((ELAPSED + 1))
|
||
done
|
||
|
||
if [ $ELAPSED -ge $TIMEOUT ] && [ -z "${SUBAGENT_SILENT:-}" ]; then
|
||
echo "⚠️ Timeout beim Warten auf Pi-Start (${TIMEOUT}s), versuche trotzdem..."
|
||
fi
|
||
|
||
# Name setzen
|
||
tmux send-keys -t "$SESSION_SLUG" "/name ${SESSION_SLUG}" Enter
|
||
sleep 1
|
||
# Orchestrator-ID ermitteln
|
||
ORCHESTRATOR_ID=$(intercom list 2>/dev/null | grep -oP '\[self.*?\]\(([a-f0-9]+)' | grep -oP '[a-f0-9]+$' 2>/dev/null || echo "")
|
||
|
||
# Aufgabe senden
|
||
if [ -n "$ORCHESTRATOR_ID" ]; then
|
||
TASK_WITH_INTERCOM="${TASK}
|
||
|
||
INTERCOM:
|
||
Orchestrator-ID: $ORCHESTRATOR_ID
|
||
Nach Abschluss: intercom send an $ORCHESTRATOR_ID mit Ergebnis
|
||
Antwort nicht an \"orchestrator\" senden — das funktioniert nicht!"
|
||
tmux send-keys -t "$SESSION_SLUG" "$TASK_WITH_INTERCOM" Enter
|
||
else
|
||
tmux send-keys -t "$SESSION_SLUG" "AUFGABE: ${TASK}" Enter
|
||
fi
|
||
|
||
if [ -z "${SUBAGENT_SILENT:-}" ]; then
|
||
echo "✅ Name und Aufgabe gesendet."
|
||
echo ""
|
||
echo "📋 Fenster ist offen — du siehst alles live."
|
||
echo ""
|
||
echo "ℹ️ Bei Rückfragen:"
|
||
echo " - Direkt ins Fenster tippen (es hat den Fokus)"
|
||
echo " - Oder: tmux attach -t $SESSION_SLUG"
|
||
echo " - Verlassen: Ctrl+B, dann D (Session läuft weiter)"
|
||
echo ""
|
||
echo "🔑 Session: $SESSION_SLUG"
|
||
echo ""
|
||
fi
|
||
|
||
# Aktuelles Fenster merken (bevor gnome-terminal den Fokus stiehlt)
|
||
SAVED_WINDOW=$(xdotool getactivewindow 2>/dev/null || echo "")
|
||
|
||
# Fenster-Offset (20px pro SubAgent nach rechts, max 9 — Überlappung vermeiden)
|
||
SUBAGENT_COUNTER_FILE="/tmp/.subagent-geo-counter"
|
||
SUBAGENT_COUNT=$(cat "$SUBAGENT_COUNTER_FILE" 2>/dev/null || echo 0)
|
||
SUBAGENT_COUNT=$((SUBAGENT_COUNT + 1))
|
||
[ "$SUBAGENT_COUNT" -gt 9 ] && SUBAGENT_COUNT=1
|
||
echo "$SUBAGENT_COUNT" > "$SUBAGENT_COUNTER_FILE"
|
||
X_OFFSET=$((4652 + (SUBAGENT_COUNT - 1) * 20))
|
||
# gnome-terminal-Fenster öffnen und an tmux-Session attach-en
|
||
gnome-terminal \
|
||
--geometry=64x24+${X_OFFSET}+100 \
|
||
--title="Subagent: $NAME" \
|
||
--working-directory="$WORKDIR" \
|
||
-- bash -c "tmux attach-session -t '$SESSION_SLUG'; echo 'Fenster kann geschlossen werden.'; read"
|
||
|
||
# Attachment-Check: Session muss innerhalb von 5s sichtbar (attached) sein.
|
||
# Wenn gnome-terminal nicht geöffnet hat (kein DISPLAY, crash etc.), Session killen.
|
||
ATTACH_TIMEOUT=5
|
||
ATTACH_ELAPSED=0
|
||
while [ $ATTACH_ELAPSED -lt $ATTACH_TIMEOUT ]; do
|
||
if tmux ls -F '#{session_name}:#{session_attached}' 2>/dev/null | grep -q "^${SESSION_SLUG}:1$"; then
|
||
break
|
||
fi
|
||
sleep 1
|
||
ATTACH_ELAPSED=$((ATTACH_ELAPSED + 1))
|
||
done
|
||
|
||
if ! tmux ls -F '#{session_name}:#{session_attached}' 2>/dev/null | grep -q "^${SESSION_SLUG}:1$"; then
|
||
echo "FEHLER: Kein sichtbares Fenster für Session '${SESSION_SLUG}' nach ${ATTACH_TIMEOUT}s."
|
||
echo " Mögliche Ursache: DISPLAY nicht gesetzt oder gnome-terminal nicht verfügbar."
|
||
echo " Session wird beendet — keine unsichtbaren Hintergrundprozesse."
|
||
tmux kill-session -t "$SESSION_SLUG" 2>/dev/null
|
||
exit 1
|
||
fi
|
||
|
||
# Fokus zurück zum ursprünglichen Terminal holen
|
||
sleep 0.3
|
||
if [ -n "$SAVED_WINDOW" ]; then
|
||
xdotool windowactivate "$SAVED_WINDOW" 2>/dev/null || true
|
||
fi
|
||
|
||
if [ -z "${SUBAGENT_SILENT:-}" ]; then
|
||
echo "✨ Fertig."
|
||
fi |