Files
bootstrap/tools/orchestrator/session-status.sh
Jason Woltje 5ba531e2d0 feat: r0 coordinator tooling for orchestrator protocol
Implements the manual coordinator workflow for multi-session agent
orchestration. Agents stop after one milestone (confirmed limitation);
these tools let the human coordinator check status, generate continuation
prompts, and chain sessions together.

New:
- tools/orchestrator/ — 5 scripts + shared library (_lib.sh)
  - mission-init.sh: initialize mission with milestones and state files
  - mission-status.sh: dashboard showing milestones, tasks, sessions
  - session-status.sh: check if agent is running/stale/dead
  - continue-prompt.sh: generate paste-ready continuation prompt
  - session-resume.sh: crash recovery with dirty state detection
- guides/ORCHESTRATOR-PROTOCOL.md: agent-facing mission lifecycle guide
- templates/docs/: mission manifest, scratchpad, continuation templates
- templates/repo/.mosaic/orchestrator/mission.json: state file template

Modified:
- bin/mosaic: add 'coord' subcommand + resume advisory on launch
- AGENTS.md: conditional loading for protocol guide + rule 37
- bin/mosaic-doctor: checks for new coordinator files
- session hooks: mission detection on start, cleanup on end

Usage: mosaic coord init|mission|status|continue|resume

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:22:50 -06:00

158 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
#
# session-status.sh — Check agent session health
#
# Usage:
# session-status.sh [--project <path>] [--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 <path>] [--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"