- Reset master to upstream/main (16,697 commits) - Overlay 2,271 local-only files (skills, tools, workspace, configs, apps) - Restore IDENTITY.md and USER.md templates - Build verified, gateway running, Discord working Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
253 lines
7.3 KiB
Bash
253 lines
7.3 KiB
Bash
#!/usr/bin/env bash
|
|
# Jarvis Auto-Delegation System
|
|
# Intelligently routes Kanban tasks to specialist agents
|
|
|
|
set -euo pipefail
|
|
|
|
KANBAN_API="http://192.168.1.220:5003/api/tasks"
|
|
KANBAN_CLI="/home/alex/clawd/tools/kanban-cli.sh"
|
|
LOG_FILE="/home/alex/clawd/logs/jarvis-delegation.log"
|
|
|
|
log() {
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Get agent-ready tasks from Kanban
|
|
get_delegatable_tasks() {
|
|
curl -sf "$KANBAN_API?status=queue" | jq -r '
|
|
.[] | select(.tags | contains("agent:")) |
|
|
"\(.id)|\(.tags)|\(.title)"
|
|
' 2>/dev/null || true
|
|
}
|
|
|
|
# Extract agent preference from tags
|
|
get_agent_from_tags() {
|
|
local tags="$1"
|
|
echo "$tags" | grep -oP 'agent:\K[a-z-]+' | head -1
|
|
}
|
|
|
|
# Get task size for timeout calculation
|
|
get_task_size() {
|
|
local tags="$1"
|
|
if echo "$tags" | grep -q "size:xs"; then
|
|
echo "300" # 5 min
|
|
elif echo "$tags" | grep -q "size:s"; then
|
|
echo "600" # 10 min
|
|
elif echo "$tags" | grep -q "size:m"; then
|
|
echo "1800" # 30 min
|
|
elif echo "$tags" | grep -q "size:l"; then
|
|
echo "3600" # 1 hour
|
|
elif echo "$tags" | grep -q "size:xl"; then
|
|
echo "7200" # 2 hours
|
|
else
|
|
echo "1800" # default 30 min
|
|
fi
|
|
}
|
|
|
|
# Map agent tag to model
|
|
map_agent_to_model() {
|
|
local agent="$1"
|
|
case "$agent" in
|
|
gemini-flash) echo "google/gemini-3-flash-preview" ;;
|
|
gemini-pro) echo "google/gemini-3-pro-preview" ;;
|
|
nvidia) echo "nvidia/qwen/qwen3-235b-a22b" ;;
|
|
deepseek) echo "nvidia/deepseek-ai/deepseek-v3.2" ;;
|
|
llama4) echo "nvidia/meta/llama-4-maverick-17b-128e-instruct" ;;
|
|
*) echo "google/gemini-3-flash-preview" ;; # fallback
|
|
esac
|
|
}
|
|
|
|
# Self-healing: analyze failure and propose fix
|
|
heal_task_failure() {
|
|
local task_id="$1"
|
|
local agent_tag="$2"
|
|
local failure_log="$3"
|
|
local retry_count="$4"
|
|
|
|
log "🔧 Analyzing task #$task_id failure (retry $retry_count/3)"
|
|
|
|
# Extract error context (last 100 lines of failure)
|
|
local error_context=$(tail -100 "$failure_log" 2>/dev/null || echo "No log available")
|
|
|
|
# Build healing prompt
|
|
local healing_prompt="**Self-Healing Task Analysis**
|
|
|
|
A Kanban task delegation failed. Diagnose and fix it.
|
|
|
|
**Task ID:** $task_id
|
|
**Agent:** $agent_tag
|
|
**Retry:** $retry_count/3
|
|
|
|
**Error Output:**
|
|
\`\`\`
|
|
$error_context
|
|
\`\`\`
|
|
|
|
**Your Tasks:**
|
|
1. Identify root cause
|
|
2. Propose specific fix (code/config/deps/params)
|
|
3. Apply fix if possible
|
|
4. Recommend: retry (yes/no)
|
|
|
|
Be concise. Make changes. Report decision."
|
|
|
|
# Spawn healing agent (sonnet for troubleshooting)
|
|
local healing_result=$(openclaw agent \
|
|
--agent free \
|
|
--model "anthropic/claude-sonnet-4-5" \
|
|
--thinking medium \
|
|
--timeout 300 \
|
|
--message "$healing_prompt" \
|
|
2>&1)
|
|
|
|
# Check if healing recommends retry
|
|
if echo "$healing_result" | grep -qi "retry.*yes\|should.*retry\|recommend.*retry"; then
|
|
log "✅ Healing suggests retry for task #$task_id"
|
|
return 0
|
|
else
|
|
log "❌ Healing recommends stop for task #$task_id"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Delegate task to agent with self-healing retry
|
|
delegate_task() {
|
|
local task_id="$1"
|
|
local task_title="$2"
|
|
local agent_tag="$3"
|
|
local timeout="$4"
|
|
|
|
local model=$(map_agent_to_model "$agent_tag")
|
|
local max_retries=3
|
|
local retry_count=0
|
|
local success=false
|
|
|
|
log "🤖 Delegating task #$task_id ($task_title) to $agent_tag (model: $model, timeout: ${timeout}s)"
|
|
|
|
# Get full task details
|
|
local task_desc=$(curl -sf "$KANBAN_API/$task_id" | jq -r '.description // ""')
|
|
|
|
# Build agent prompt
|
|
local prompt="Kanban Task #$task_id: $task_title
|
|
|
|
$task_desc
|
|
|
|
**Instructions:**
|
|
1. Analyze the task requirements
|
|
2. Execute necessary actions
|
|
3. Report completion with summary
|
|
4. If you need approval or encounter blockers, stop and ask
|
|
|
|
When done, update Kanban status to 'done' using:
|
|
bash /home/alex/clawd/tools/kanban-cli.sh update $task_id --status done
|
|
"
|
|
|
|
# Update Kanban status to in_progress
|
|
"$KANBAN_CLI" update "$task_id" --status in_progress 2>/dev/null || true
|
|
|
|
# Self-healing retry loop
|
|
while [[ $retry_count -le $max_retries ]]; do
|
|
if [[ $retry_count -gt 0 ]]; then
|
|
log "🔄 Retry $retry_count/$max_retries for task #$task_id"
|
|
fi
|
|
|
|
# Create temp log for this attempt
|
|
local attempt_log="/tmp/jarvis-task-$task_id-attempt-$retry_count.log"
|
|
|
|
# Spawn isolated agent
|
|
set +e
|
|
openclaw agent \
|
|
--agent free \
|
|
--isolated \
|
|
--model "$model" \
|
|
--thinking low \
|
|
--timeout "$timeout" \
|
|
--message "$prompt" \
|
|
--label "kanban-task-$task_id" \
|
|
2>&1 | tee -a "$LOG_FILE" > "$attempt_log"
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
# Check for success
|
|
if [[ $exit_code -eq 0 ]]; then
|
|
# Verify task completion heuristically
|
|
if grep -qiE "(completed|done|success|finished|✅)" "$attempt_log"; then
|
|
log "✅ Task #$task_id completed successfully"
|
|
success=true
|
|
break
|
|
fi
|
|
fi
|
|
|
|
# Task failed
|
|
log "⚠️ Task #$task_id failed (exit: $exit_code)"
|
|
|
|
# Attempt healing if retries remain
|
|
if [[ $retry_count -lt $max_retries ]]; then
|
|
if heal_task_failure "$task_id" "$agent_tag" "$attempt_log" "$((retry_count + 1))"; then
|
|
retry_count=$((retry_count + 1))
|
|
sleep 5 # Brief pause before retry
|
|
continue
|
|
else
|
|
log "❌ Healing failed - stopping retries for task #$task_id"
|
|
break
|
|
fi
|
|
else
|
|
log "❌ Max retries exceeded for task #$task_id"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ $success == true ]]; then
|
|
log "🎉 Task #$task_id succeeded after $retry_count retries"
|
|
else
|
|
log "💥 Task #$task_id failed after $retry_count attempts"
|
|
"$KANBAN_CLI" update "$task_id" --status queue 2>/dev/null || true # Return to queue
|
|
fi
|
|
}
|
|
|
|
# Main delegation loop
|
|
main() {
|
|
log "=== JARVIS AUTO-DELEGATION STARTED ==="
|
|
|
|
local tasks=$(get_delegatable_tasks)
|
|
|
|
if [ -z "$tasks" ]; then
|
|
log "📭 No delegatable tasks found"
|
|
exit 0
|
|
fi
|
|
|
|
local delegated=0
|
|
local max_concurrent=3
|
|
|
|
while IFS='|' read -r task_id tags title; do
|
|
# Check concurrent limit
|
|
local running=$(pgrep -f "kanban-task-" | wc -l)
|
|
if [ "$running" -ge "$max_concurrent" ]; then
|
|
log "⏸️ Max concurrent tasks ($max_concurrent) reached, stopping"
|
|
break
|
|
fi
|
|
|
|
local agent=$(get_agent_from_tags "$tags")
|
|
if [ -z "$agent" ]; then
|
|
log "⚠️ Task #$task_id has no valid agent tag, skipping"
|
|
continue
|
|
fi
|
|
|
|
local timeout=$(get_task_size "$tags")
|
|
|
|
delegate_task "$task_id" "$title" "$agent" "$timeout"
|
|
((delegated++))
|
|
|
|
# Rate limit
|
|
sleep 2
|
|
|
|
done <<< "$tasks"
|
|
|
|
log "=== DELEGATION COMPLETE: $delegated tasks delegated ==="
|
|
}
|
|
|
|
# Create log directory
|
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
|
|
main "$@"
|