Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions .agents/scripts/supervisor-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ Usage:
supervisor-helper.sh dashboard [--batch id] [--interval N] Live TUI dashboard
supervisor-helper.sh queue-health [--batch id] Update pinned queue health issue (t1013)
supervisor-helper.sh labels [--action X] [--model Y] [--json] Query model usage labels (t1010)
supervisor-helper.sh ai-pipeline [full|dry-run] Run AI reasoning + action pipeline manually
supervisor-helper.sh ai-status Show AI supervisor status and next-run countdown
supervisor-helper.sh db [sql] Direct SQLite access
supervisor-helper.sh help Show this help

Expand Down Expand Up @@ -451,6 +453,9 @@ Environment:
SUPERVISOR_SELF_MEM_LIMIT MB before supervisor respawns after batch (default: 8192)
SUPERVISOR_SKILL_UPDATE_PR Enable skill update PR pipeline in pulse (default: false)
SUPERVISOR_SKILL_UPDATE_INTERVAL Seconds between skill update PR runs (default: 86400)
SUPERVISOR_AI_ENABLED Enable AI supervisor reasoning in pulse (default: true)
SUPERVISOR_AI_INTERVAL Pulses between AI reasoning runs (default: 15, ~30min)
AI_MAX_ACTIONS_PER_CYCLE Max actions per AI reasoning cycle (default: 10)
AIDEVOPS_SUPERVISOR_DIR Override supervisor data directory

Database: ~/.aidevops/.agent-workspace/supervisor/supervisor.db
Expand Down Expand Up @@ -735,19 +740,43 @@ main() {
;;
ai-status)
local last_run_ts
last_run_ts=$(db "$SUPERVISOR_DB" "SELECT MAX(timestamp) FROM state_log WHERE task_id = 'ai-supervisor' AND to_state = 'complete';" 2>/dev/null || echo "never")
last_run_ts=$(db "$SUPERVISOR_DB" "SELECT MAX(timestamp) FROM state_log WHERE task_id = 'ai-supervisor' AND to_state = 'complete';" 2>/dev/null || echo "")
local run_count
run_count=$(db "$SUPERVISOR_DB" "SELECT COUNT(*) FROM state_log WHERE task_id = 'ai-supervisor' AND to_state = 'complete';" 2>/dev/null || echo 0)
local action_count
action_count=$(db "$SUPERVISOR_DB" "SELECT COUNT(*) FROM state_log WHERE task_id = 'ai-supervisor' AND from_state = 'actions';" 2>/dev/null || echo 0)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The default value 15 for SUPERVISOR_AI_INTERVAL is hardcoded here. To improve maintainability and ensure consistency, consider defining this as a shared constant in shared-constants.sh if it's used in multiple places or if its value is subject to change. This aligns with the principle of avoiding magic numbers.


# Read pulse counter and last-run timestamp from files
local ai_pulse_count_file="${SUPERVISOR_DIR}/ai-pulse-count"
local ai_last_run_file="${SUPERVISOR_DIR}/ai-supervisor-last-run"
local ai_log_file="${SUPERVISOR_DIR}/logs/ai-supervisor.log"
local ai_interval="${SUPERVISOR_AI_INTERVAL:-15}"

local ai_pulse_count=0
if [[ -f "$ai_pulse_count_file" ]]; then
ai_pulse_count=$(cat "$ai_pulse_count_file" 2>/dev/null || echo 0)
fi

local ai_last_run="never"
if [[ -f "$ai_last_run_file" ]]; then
ai_last_run=$(cat "$ai_last_run_file" 2>/dev/null || echo "never")
elif [[ -n "$last_run_ts" ]]; then
ai_last_run="$last_run_ts"
fi

local ai_pulses_remaining=$((ai_interval - ai_pulse_count))
local ai_minutes_remaining=$((ai_pulses_remaining * 2))

echo "AI Supervisor Status"
echo " Last run: ${last_run_ts:-never}"
echo " Total reasoning runs: $run_count"
echo " Total action executions: $action_count"
echo " Enabled: ${SUPERVISOR_AI_ENABLED:-true}"
echo " Interval: ${SUPERVISOR_AI_INTERVAL:-15} pulses (~$((${SUPERVISOR_AI_INTERVAL:-15} * 2))min)"
echo " Enabled: ${SUPERVISOR_AI_ENABLED:-true}"
echo " Interval: ${ai_interval} pulses (~$((ai_interval * 2))min)"
echo " Pulse counter: ${ai_pulse_count}/${ai_interval} (${ai_pulses_remaining} pulses / ~${ai_minutes_remaining}min until next run)"
echo " Last run: ${ai_last_run}"
echo " Total runs: $run_count"
echo " Total actions: $action_count"
echo " Max actions/cycle: ${AI_MAX_ACTIONS_PER_CYCLE:-10}"
echo " Log dir: ${AI_REASON_LOG_DIR:-$HOME/.aidevops/logs/ai-supervisor}"
echo " Log file: ${ai_log_file}"
echo " Reason log dir: ${AI_REASON_LOG_DIR:-$HOME/.aidevops/logs/ai-supervisor}"
;;
help | --help | -h) show_usage ;;
*)
Expand Down
83 changes: 83 additions & 0 deletions .agents/scripts/supervisor/pulse.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1858,6 +1858,89 @@ RULES:
fi
fi

# Phase 14: AI Supervisor reasoning + action execution (t1085.5)
# Runs the full AI pipeline (ai-reason.sh -> ai-actions.sh) on a configurable
# pulse interval. Default: every 15 pulses (~30 min at 2-min cron cadence).
# Controlled by SUPERVISOR_AI_ENABLED (default: true) and
# SUPERVISOR_AI_INTERVAL (default: 15 pulses).
# Dedicated log: $SUPERVISOR_DIR/logs/ai-supervisor.log
local ai_enabled="${SUPERVISOR_AI_ENABLED:-true}"
if [[ "$ai_enabled" == "true" ]]; then
local ai_interval="${SUPERVISOR_AI_INTERVAL:-15}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The default value 15 for SUPERVISOR_AI_INTERVAL is hardcoded here. To improve maintainability and ensure consistency, consider defining this as a shared constant in shared-constants.sh if it's used in multiple places or if its value is subject to change. This aligns with the principle of avoiding magic numbers.

local ai_pulse_count_file="${SUPERVISOR_DIR}/ai-pulse-count"
local ai_last_run_file="${SUPERVISOR_DIR}/ai-supervisor-last-run"
local ai_log_dir="${SUPERVISOR_DIR}/logs"
local ai_log_file="${ai_log_dir}/ai-supervisor.log"

# Read and increment pulse counter
local ai_pulse_count=0
if [[ -f "$ai_pulse_count_file" ]]; then
ai_pulse_count=$(cat "$ai_pulse_count_file" 2>/dev/null || echo 0)
fi
ai_pulse_count=$((ai_pulse_count + 1))
echo "$ai_pulse_count" >"$ai_pulse_count_file" 2>/dev/null || true

# Check if it's time to run the AI pipeline
if [[ "$ai_pulse_count" -ge "$ai_interval" ]]; then
# Reset counter
echo "0" >"$ai_pulse_count_file" 2>/dev/null || true

# Determine repo path
local ai_repo_path=""
ai_repo_path=$(db "$SUPERVISOR_DB" "SELECT DISTINCT repo FROM tasks LIMIT 1;" 2>/dev/null || echo "")
if [[ -z "$ai_repo_path" ]]; then
ai_repo_path="$(pwd)"
fi

log_info " Phase 14: AI supervisor reasoning + action execution (pulse $ai_pulse_count/$ai_interval)"

# Ensure log directory exists
mkdir -p "$ai_log_dir" 2>/dev/null || true

# Record start timestamp
local ai_start_ts
ai_start_ts=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
{
echo ""
echo "=== AI Supervisor Run: $ai_start_ts ==="
} >>"$ai_log_file" 2>/dev/null || true

# Run the full AI pipeline (reasoning -> action execution)
local ai_result=""
local ai_rc=0
ai_result=$(run_ai_actions_pipeline "$ai_repo_path" "full" 2>>"$ai_log_file") || ai_rc=$?

# Record completion timestamp
local ai_end_ts
ai_end_ts=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
echo "$ai_end_ts" >"$ai_last_run_file" 2>/dev/null || true

if [[ $ai_rc -eq 0 ]]; then
# Extract summary from result JSON
local ai_executed ai_failed ai_skipped
ai_executed=$(printf '%s' "$ai_result" | jq -r '.executed // 0' 2>/dev/null || echo 0)
ai_failed=$(printf '%s' "$ai_result" | jq -r '.failed // 0' 2>/dev/null || echo 0)
ai_skipped=$(printf '%s' "$ai_result" | jq -r '.skipped // 0' 2>/dev/null || echo 0)
log_success " Phase 14: AI pipeline complete (executed=$ai_executed failed=$ai_failed skipped=$ai_skipped)"
{
echo "Result: executed=$ai_executed failed=$ai_failed skipped=$ai_skipped"
echo "=== End: $ai_end_ts ==="
} >>"$ai_log_file" 2>/dev/null || true
else
log_warn " Phase 14: AI pipeline returned rc=$ai_rc (see $ai_log_file)"
{
echo "Result: rc=$ai_rc (pipeline error)"
echo "=== End: $ai_end_ts ==="
} >>"$ai_log_file" 2>/dev/null || true
fi
else
local ai_pulses_remaining=$((ai_interval - ai_pulse_count))
log_verbose " Phase 14: AI pipeline skipped (${ai_pulses_remaining} pulses until next run, interval=${ai_interval})"
fi
else
log_verbose " Phase 14: AI pipeline disabled (SUPERVISOR_AI_ENABLED=false)"
fi

# t1052: Clear deferred batch completion flag to avoid leaking state
# if the supervisor process is reused for non-pulse commands
_PULSE_DEFER_BATCH_COMPLETION=""
Expand Down
Loading