#!/usr/bin/env bash set -euo pipefail # # session-status.sh — Check agent session health # # Usage: # session-status.sh [--project ] [--format table|json] # # Exit codes: # 0 = running # 2 = stale (recently died) # 3 = dead (no longer running) # 4 = no session SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/_lib.sh" # ─── Parse arguments ───────────────────────────────────────────────────────── PROJECT="." FORMAT="table" while [[ $# -gt 0 ]]; do case "$1" in --project) PROJECT="$2"; shift 2 ;; --format) FORMAT="$2"; shift 2 ;; -h|--help) echo "Usage: session-status.sh [--project ] [--format table|json]" exit 0 ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac done _require_jq # ─── Check session lock ───────────────────────────────────────────────────── lock_data="" if ! lock_data="$(session_lock_read "$PROJECT")"; then if [[ "$FORMAT" == "json" ]]; then echo '{"status":"no-session"}' else echo -e "${C_DIM}No active session.${C_RESET}" fi exit 4 fi # Parse lock session_id="$(echo "$lock_data" | jq -r '.session_id // "unknown"')" runtime="$(echo "$lock_data" | jq -r '.runtime // "unknown"')" pid="$(echo "$lock_data" | jq -r '.pid // 0')" started_at="$(echo "$lock_data" | jq -r '.started_at // ""')" milestone_id="$(echo "$lock_data" | jq -r '.milestone_id // ""')" # ─── Determine status ─────────────────────────────────────────────────────── status="unknown" exit_code=1 if is_pid_alive "$pid"; then status="running" exit_code=0 else # PID is dead — check how recently last_act="$(last_activity_time "$PROJECT")" now="$(epoch_now)" age=$(( now - last_act )) if (( age < STALE_THRESHOLD )); then status="stale" exit_code=2 elif (( age < DEAD_THRESHOLD )); then status="stale" exit_code=2 else status="dead" exit_code=3 fi fi # ─── Gather supplementary info ────────────────────────────────────────────── duration_secs=0 if [[ -n "$started_at" ]]; then start_epoch="$(iso_to_epoch "$started_at")" now="$(epoch_now)" duration_secs=$(( now - start_epoch )) fi last_act="$(last_activity_time "$PROJECT")" # Current milestone from mission.json current_ms="" if [[ -f "$(mission_path "$PROJECT")" ]]; then current_ms="$(current_milestone_id "$PROJECT")" if [[ -n "$current_ms" ]]; then ms_name="$(milestone_name "$PROJECT" "$current_ms")" [[ -n "$ms_name" ]] && current_ms="$current_ms ($ms_name)" fi fi # Next task from TASKS.md next_task="$(find_next_task "$PROJECT")" # ─── Output ────────────────────────────────────────────────────────────────── if [[ "$FORMAT" == "json" ]]; then jq -n \ --arg status "$status" \ --arg session_id "$session_id" \ --arg runtime "$runtime" \ --arg pid "$pid" \ --arg started_at "$started_at" \ --arg duration "$duration_secs" \ --arg milestone "$current_ms" \ --arg next_task "$next_task" \ --arg last_activity "$last_act" \ '{ status: $status, session_id: $session_id, runtime: $runtime, pid: ($pid | tonumber), started_at: $started_at, duration_seconds: ($duration | tonumber), milestone: $milestone, next_task: $next_task, last_activity_epoch: ($last_activity | tonumber) }' else # Color the status case "$status" in running) status_color="${C_GREEN}RUNNING${C_RESET}" ;; stale) status_color="${C_YELLOW}STALE${C_RESET}" ;; dead) status_color="${C_RED}DEAD${C_RESET}" ;; *) status_color="$status" ;; esac echo "" echo -e " Session Status: $status_color ($runtime)" echo -e " ${C_CYAN}Session ID:${C_RESET} $session_id" echo -e " ${C_CYAN}Started:${C_RESET} $started_at ($(format_duration "$duration_secs"))" echo -e " ${C_CYAN}PID:${C_RESET} $pid" [[ -n "$current_ms" ]] && echo -e " ${C_CYAN}Milestone:${C_RESET} $current_ms" [[ -n "$next_task" ]] && echo -e " ${C_CYAN}Next task:${C_RESET} $next_task" echo -e " ${C_CYAN}Last activity:${C_RESET} $(format_ago "$last_act")" echo "" if [[ "$status" == "stale" || "$status" == "dead" ]]; then echo -e " ${C_YELLOW}Session is no longer running.${C_RESET}" echo " Recovery: mosaic coord resume" echo " Continue: mosaic coord continue" echo "" fi fi exit "$exit_code"