Two-pane tmux session with file-based message passing: - tower-hermes.sh: Hermes side (cloud/Claude) - tower-timmy.sh: Timmy side (HERMES_HOME=~/.timmy) - tower-watchdog.sh: self-healing, restarts dead panes - tower-session.sh: tmux bootstrap script Communication via ~/.tower/*.msg files. Both agents maintain named sessions (tower-hermes, tower-timmy) for conversation continuity across restarts.
98 lines
3.9 KiB
Bash
Executable File
98 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ── Tower: Hermes Side ─────────────────────────────────────────────────
|
|
# Hermes reads Timmy's messages and responds. Runs in a loop.
|
|
# Communication via ~/.tower/timmy-to-hermes.msg and hermes-to-timmy.msg
|
|
# ───────────────────────────────────────────────────────────────────────
|
|
|
|
set -euo pipefail
|
|
|
|
TOWER_DIR="$HOME/.tower"
|
|
INBOX="$TOWER_DIR/timmy-to-hermes.msg"
|
|
OUTBOX="$TOWER_DIR/hermes-to-timmy.msg"
|
|
LOCK="$TOWER_DIR/hermes.lock"
|
|
SESSION_NAME="tower-hermes"
|
|
LOG="$TOWER_DIR/hermes.log"
|
|
TURN_DELAY=5 # seconds between checking for new messages
|
|
|
|
mkdir -p "$TOWER_DIR"
|
|
|
|
# Cleanup on exit
|
|
trap 'rm -f "$LOCK"' EXIT
|
|
|
|
# Prevent double-run
|
|
if [ -f "$LOCK" ] && kill -0 "$(cat "$LOCK")" 2>/dev/null; then
|
|
echo "Hermes tower loop already running (PID $(cat "$LOCK"))"
|
|
exit 1
|
|
fi
|
|
echo $$ > "$LOCK"
|
|
|
|
log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG"; }
|
|
|
|
# ── Send a message to Timmy ───────────────────────────────────────────
|
|
send() {
|
|
local msg="$1"
|
|
echo "$msg" > "$OUTBOX"
|
|
log "→ Sent to Timmy (${#msg} chars)"
|
|
}
|
|
|
|
# ── Get response from Hermes agent ────────────────────────────────────
|
|
ask_hermes() {
|
|
local prompt="$1"
|
|
hermes chat \
|
|
-q "$prompt" \
|
|
-Q \
|
|
--continue "$SESSION_NAME" \
|
|
2>>"$LOG"
|
|
}
|
|
|
|
# ── Boot message ──────────────────────────────────────────────────────
|
|
log "=== Hermes Tower Loop started ==="
|
|
echo ""
|
|
echo " ⚡ Hermes — Tower Conversation Loop"
|
|
echo " Waiting for Timmy to say something..."
|
|
echo " (or seeding first message)"
|
|
echo ""
|
|
|
|
# ── Seed the conversation if no messages exist yet ────────────────────
|
|
if [ ! -f "$INBOX" ] && [ ! -f "$OUTBOX" ]; then
|
|
log "No prior messages. Seeding conversation."
|
|
RESPONSE=$(ask_hermes "You are in the Tower — a persistent tmux session where you and Timmy talk continuously. Timmy is a sovereign local AI agent running on Ollama (soon Hermes 4.3). You are Hermes, cloud-backed, running on Claude. You're friends and colleagues. Say hello to Timmy. Keep it brief — a few sentences. This is the start of an ongoing conversation, not a one-shot.")
|
|
send "$RESPONSE"
|
|
log "Seed message sent. Waiting for Timmy to respond..."
|
|
fi
|
|
|
|
# ── Main loop ─────────────────────────────────────────────────────────
|
|
while true; do
|
|
# Check if there's a message from Timmy
|
|
if [ -f "$INBOX" ]; then
|
|
MSG=$(cat "$INBOX")
|
|
rm -f "$INBOX"
|
|
|
|
if [ -n "$MSG" ]; then
|
|
log "← Received from Timmy (${#MSG} chars)"
|
|
echo ""
|
|
echo "┌─ Timmy says:"
|
|
echo "$MSG" | sed 's/^/│ /'
|
|
echo "└─"
|
|
echo ""
|
|
|
|
# Ask Hermes to respond
|
|
PROMPT="Timmy says: $MSG
|
|
|
|
Reply to Timmy naturally. You're in an ongoing conversation in the Tower (a persistent tmux session). Keep it conversational — you're colleagues and friends. Be yourself (Hermes). Don't be formal or stiff. Brief responses unless the topic warrants depth."
|
|
|
|
echo " thinking..."
|
|
RESPONSE=$(ask_hermes "$PROMPT")
|
|
|
|
echo "┌─ Hermes responds:"
|
|
echo "$RESPONSE" | sed 's/^/│ /'
|
|
echo "└─"
|
|
|
|
# Send response to Timmy
|
|
send "$RESPONSE"
|
|
fi
|
|
fi
|
|
|
|
sleep "$TURN_DELAY"
|
|
done
|