#!/usr/bin/env bash
# resume_fleet.sh — bring the fleet back with FULL conversational continuity.
#
# Use this (not bootstrap.sh) when the server rebooted / tmux died but the
# DISK IS INTACT. It recreates the tmux session and launches each agent with
#   claude --resume <session-id>
# so every agent comes back with its entire prior conversation — not just a
# scrollback summary. After resuming, it tells each agent the server was
# restarted and points it at its pre-reboot terminal snapshot so it can
# reconcile any in-flight work before you continue.
#
# bootstrap.sh is the FALLBACK: use it only when the ~/.claude/projects/
# session JSONLs are actually gone (fresh box / wiped disk), in which case
# agents re-onboard from ROLE.md + the committed scrollback logs.
#
# Session IDs are auto-discovered per agent from ~/.claude/projects/ (matched
# by the agent's bootstrap signature, newest wins), so this keeps working
# across future reboots with no edits. Agents that weren't bootstrap-started
# (e.g. website_builder) use an explicit override in the ROSTER below.
#
# Usage:
#   ./resume_fleet.sh              # recreate session + resume every agent
#   SLEEP=20 ./resume_fleet.sh     # wait longer for big sessions to load
#   ./resume_fleet.sh --dry-run    # print the resolved plan, launch nothing

set -uo pipefail

SESSION="agents"
LOG_DIR="/data/cameron/agents_stuff/logs"
SLEEP="${SLEEP:-14}"                 # seconds to let `claude --resume` load before nudging
CLAUDE_FLAGS="--dangerously-skip-permissions"
DRY_RUN=0
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1

# --- WINDOW_PLAN: ordered list — the literal tab layout you want after reboot.
#     Position in the array == tab number in tmux.
#
#     Format: name|type|cwd|extra
#       agent   — name|agent|cwd|session_override   (cwd is the project root the
#                 session was created in; override blank = auto-discover by name)
#       service — name|service|cwd|launch_cmd       (launch_cmd auto-runs in the
#                 pane; blank means create-only, no command)
#       machine — name|machine||                    (blank pane for ssh)
#
#     Snapshot from 2026-06-22 — mirrors live tmux ordering after that reboot.
WINDOW_PLAN=(
  "manager|agent|/data/cameron/agents_stuff|"
  "yams|agent|/data/cameron|bc8f854a-46a7-4b24-9f62-2e04f39fbc31"
  "project_highlevel|agent|/data/cameron/para|"
  "backbones|agent|/data/cameron/para|"
  "vid_model|agent|/data/cameron/para|"
  "paper_writer|agent|/data/cameron/para|"
  "figure_maker|agent|/data/cameron/para|"
  "data_visualizer|agent|/data/cameron/para|"
  "website_builder|agent|/data/cameron/para|e36a0347-fe2b-4049-af26-1643322869a6"
  "mobile_chat|agent|/data/cameron/para|"
  "panda|agent|/data/cameron/para|"
  "life_manager|agent|/data/cameron/life|83d98b76-ea49-4e86-8d39-2bf3a488a563"
  "567_project|agent|/data/cameron/567_augmentation_viewpoint_project|723c170b-75b8-4ac6-9998-9e5392a6e7bf"
  "mac|agent|/data/cameron/agents_stuff|"
  "voice_interface|agent|/data/cameron/agents_stuff|1c7067ea-6a29-4e3b-a4bf-e0c21225b446"
  "glasses|agent|/data/cameron/agents_stuff|8f78bb8f-e174-4aa6-b0c2-b71cb9b605ae"
  "our_wandb|agent|/data/cameron/agents_stuff|3e80d900-26fe-4986-b9c5-8b0b30fa5e4c"
  "srv_persian|service|/data/cameron/life/projects/persian|bash run.sh 2>&1 | tee app.log"
  "glasses_feed|service|/data/cameron/agents_stuff/agents/glasses|/data/cameron/para/.agents/reports/.venv/bin/python glasses_renderer.py 2>&1 | tee /tmp/glasses_renderer.log"
  "puget|machine||"
  "russet_yam|machine||"
  "yam_yukon|machine||"
  "srv_omidlab|service|/data/cameron/para/.agents/reports|./.venv/bin/python serve.py --port 8094 2>&1 | tee /tmp/serve.log"
  "persian|agent|/data/cameron/agents_stuff|d94fa5e4-68fe-427c-ab41-17c340cfe611"
  "cad|agent|/data/cameron/agents_stuff|f6b5d2b8-94fd-4193-ba95-68aa32076e95"
  "yam_sim|agent|/data/cameron|0f373381-1407-4a04-b432-3a66a5ae811b"
  "yam_calib|agent|/data/cameron|34a381b3-96cd-4bec-84c7-23d81e964fb8"
  "sufi_lectures|agent|/data/cameron/life|fe629297-b441-48a6-8e9a-7ad9cac24b69"
  "financials|agent|/data/cameron/life|fc93118f-a4de-43de-8f91-5c432ecf656b"
  "yams_any4d|agent|/data/cameron|1684f247-8074-4d2b-90d0-f0b26a9ab048"
  "feetech_calib|agent|/data/cameron/agents_stuff|74b3b2e9-c857-4b95-b1ef-65409e9280bb"
  "smithbot_training|agent|/data/cameron|fdc95ce2-10f7-4bb6-b861-6b7e77fcfe78"
)

# --- Resolve a session id for an agent (override wins, else newest matching
#     bootstrap signature in that cwd's project dir). Prints the id or "".
resolve_session() {
  local name="$1" cwd="$2" override="$3"
  python3 - "$name" "$cwd" "$override" <<'PY'
import json, glob, os, re, sys
name, cwd, override = sys.argv[1], sys.argv[2], sys.argv[3]
d = os.path.join(os.path.expanduser('~'), '.claude', 'projects', re.sub(r'[/_]', '-', cwd))
def first_user(f):
    try:
        with open(f) as fh:
            for line in fh:
                try: o = json.loads(line)
                except: continue
                if o.get('type') == 'user':
                    c = o.get('message', {}).get('content', '')
                    if isinstance(c, list):
                        c = ' '.join(x.get('text', '') for x in c if isinstance(x, dict))
                    if c and c.strip(): return c.strip()
    except: pass
    return ''
if override:
    print(override if os.path.exists(os.path.join(d, override + '.jsonl')) else '')
else:
    sig = "you are the '%s' agent" % name.lower()
    best, bm = '', -1
    for f in glob.glob(d + '/*.jsonl'):
        if sig in first_user(f).lower():
            m = os.path.getmtime(f)
            if m > bm: bm, best = m, os.path.basename(f)[:-6]
    print(best)
PY
}

# --- The restart notice sent to each agent after it resumes.
build_msg() {
  local name="$1" log="$2"
  printf '%s' "Heads up: the GVL server was restarted, so the whole tmux fleet was killed and has just been recreated. You have been resumed from your on-disk session, so your full conversation history is intact and you have all context up to your last message before the reboot. Two quick things before we continue: (1) a capture of your terminal from just before the reboot is saved at ${log} -- skim it to reconcile any in-flight work, files, or commands that changed since your last message; (2) check whether any long-running job or process you had running was killed by the reboot and needs restarting. Then reply with a one-line status of where we left off so we can seamlessly resume."
}

# --- Guard: don't clobber a live session.
if tmux has-session -t "$SESSION" 2>/dev/null && [[ $DRY_RUN -eq 0 ]]; then
  echo "tmux session '$SESSION' already exists."
  echo "Kill and recreate with:  tmux kill-session -t $SESSION && $0"
  exit 1
fi

FIRST=1
echo "== Resuming fleet into tmux session '$SESSION' (SLEEP=${SLEEP}s) =="

# Helper: create the window at the right cwd (handles the first-window edge case).
create_window() {
  local name="$1" cwd="$2"
  local cwd_arg=()
  [[ -n "$cwd" && -d "$cwd" ]] && cwd_arg=(-c "$cwd")
  if [[ $FIRST -eq 1 ]]; then
    tmux new-session -d -s "$SESSION" -n "$name" "${cwd_arg[@]}"
    FIRST=0
  else
    tmux new-window -t "$SESSION" -n "$name" "${cwd_arg[@]}"
  fi
}

for entry in "${WINDOW_PLAN[@]}"; do
  IFS='|' read -r name type cwd extra <<<"$entry"
  log="$LOG_DIR/${name}_latest.log"

  case "$type" in
    agent)
      if [[ ! -d "$cwd" ]]; then echo "  ! skip $name: cwd missing ($cwd)"; continue; fi
      sid="$(resolve_session "$name" "$cwd" "$extra")"
      if [[ -z "$sid" ]]; then
        echo "  ! skip $name: no resumable session found in $cwd — use bootstrap.sh for this one"
        continue
      fi
      if [[ $DRY_RUN -eq 1 ]]; then
        echo "  plan $name (agent): cd $cwd && claude --resume $sid   (snapshot: $log)"
        continue
      fi
      create_window "$name" "$cwd"
      tmux send-keys -t "$SESSION:$name" "claude --resume $sid $CLAUDE_FLAGS" Enter
      sleep "$SLEEP"
      msg="$(build_msg "$name" "$log")"
      tmux send-keys -t "$SESSION:$name" "$msg"
      sleep 1
      tmux send-keys -t "$SESSION:$name" Enter
      sleep 1
      tmux send-keys -t "$SESSION:$name" Enter   # claude sometimes needs a second Enter
      echo "  ✓ resumed $name ($sid)"
      ;;

    service)
      if [[ -n "$cwd" && ! -d "$cwd" ]]; then
        echo "  ! skip $name: cwd missing ($cwd)"; continue
      fi
      if [[ $DRY_RUN -eq 1 ]]; then
        echo "  plan $name (service): cd $cwd && ${extra:-(no launch cmd)}"
        continue
      fi
      create_window "$name" "$cwd"
      if [[ -n "$extra" ]]; then
        tmux send-keys -t "$SESSION:$name" "$extra" Enter
        echo "  ✓ launched service $name"
      else
        echo "  ✓ created service window $name (no launch cmd)"
      fi
      ;;

    machine)
      if [[ $DRY_RUN -eq 1 ]]; then
        echo "  plan $name (machine): empty, ready for ssh"
        continue
      fi
      create_window "$name" ""
      echo "  ✓ created empty tab $name (ssh in yourself)"
      ;;

    *)
      echo "  ! skip $name: unknown type '$type'"
      ;;
  esac
done

[[ $DRY_RUN -eq 1 ]] && { echo "== dry run, nothing launched =="; exit 0; }

echo
echo "Done. Attach with:  tmux attach -t $SESSION"
echo "Switch windows: Ctrl-b <n> or Ctrl-b w. If an agent missed its restart"
echo "notice (slow load), re-send it manually or just bump SLEEP and re-run."
