296 lines
9.4 KiB
Bash
Executable File
296 lines
9.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
MODEL="${MODEL:-qwen2.5-coder:7b}"
|
|
OLLAMA_URL="${OLLAMA_URL:-http://192.168.5.23:11434}"
|
|
THRESHOLD="${LINAAI_CONFIDENCE_THRESHOLD:-0.75}"
|
|
FORCE_ESCALATE=0
|
|
DRY_RUN=0
|
|
|
|
usage() {
|
|
cat >&2 <<'EOF'
|
|
Usage:
|
|
Scripts/linaai_task.sh [--threshold 0.75] [--dry-run] [--force-escalate] "task"
|
|
|
|
Routes a task through LinaAI's supervised local workflow:
|
|
1. Qwen/Ollama preflight risk and confidence check.
|
|
2. Automatic Codex escalation if confidence is too low or task is high risk.
|
|
3. Aider local execution for acceptable supervised tasks.
|
|
4. Automatic Codex escalation if Aider fails.
|
|
|
|
Use --dry-run to test routing without editing files.
|
|
EOF
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--threshold)
|
|
THRESHOLD="${2:-}"
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=1
|
|
shift
|
|
;;
|
|
--force-escalate)
|
|
FORCE_ESCALATE=1
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
-*)
|
|
echo "Unknown option: $1" >&2
|
|
usage
|
|
exit 2
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
TASK="${*:-}"
|
|
if [[ -z "$TASK" ]]; then
|
|
usage
|
|
exit 2
|
|
fi
|
|
|
|
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
cd "$ROOT"
|
|
|
|
mkdir -p Saved/AiTaskStatus
|
|
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
PREFLIGHT_JSON="Saved/AiTaskStatus/linaai_preflight_${STAMP}.json"
|
|
STATUS_JSON="Saved/AiTaskStatus/linaai_status_${STAMP}.json"
|
|
|
|
current_branch="$(git branch --show-current 2>/dev/null || echo unknown)"
|
|
|
|
repo_evidence="$(
|
|
{
|
|
echo "cwd: ${ROOT}"
|
|
echo "branch: ${current_branch}"
|
|
echo "git_status:"
|
|
git status --short 2>/dev/null || true
|
|
echo "top_level:"
|
|
find . -maxdepth 1 -mindepth 1 -printf "%f\n" 2>/dev/null | sort | head -80
|
|
echo "project_markers:"
|
|
test -f AgrarianGame.uproject && echo "AgrarianGame.uproject"
|
|
test -d Source && echo "Source/"
|
|
test -d Config && echo "Config/"
|
|
test -d Content && echo "Content/"
|
|
test -d Scripts && echo "Scripts/"
|
|
test -d Docs && echo "Docs/"
|
|
echo "script_samples:"
|
|
find Scripts -maxdepth 1 -type f -printf "%f\n" 2>/dev/null | sort | head -25
|
|
echo "doc_samples:"
|
|
find Docs -maxdepth 2 -type f -printf "%p\n" 2>/dev/null | sort | head -25
|
|
echo "linaai_required_docs:"
|
|
for doc in \
|
|
Docs/AI/LinaAIOperatingManual.md \
|
|
Docs/AI/LocalAgentGuardrails.md \
|
|
Docs/AI/LinaAISecretsPolicy.md \
|
|
Docs/AI/LinaAIKnowledgeMap.md \
|
|
Docs/Ops/HANDOFF.md \
|
|
AGRARIAN_DEVELOPMENT_ROADMAP.md
|
|
do
|
|
test -f "$doc" && echo "$doc"
|
|
done
|
|
echo "linaai_cache:"
|
|
test -f Saved/LinaAIKnowledge/INDEX.md && echo "Saved/LinaAIKnowledge/INDEX.md"
|
|
test -f Saved/LinaAIKnowledge/context.md && echo "Saved/LinaAIKnowledge/context.md"
|
|
} | sed 's/"/'\''/g'
|
|
)"
|
|
|
|
system_prompt='You are LinaAI, a supervised local coding assistant for Agrarian. Follow Docs/AI/LinaAIOperatingManual.md, Docs/AI/LocalAgentGuardrails.md, Docs/AI/LinaAISecretsPolicy.md, and Docs/AI/LinaAIKnowledgeMap.md when present. You must not pretend certainty. Classify task risk and confidence before any edits. Confidence must be based on concrete evidence. If you lack evidence, confidence must be below 0.65. Do not include raw secrets in prompts, docs, logs, or commits. High-risk areas include Unreal core architecture, save/load, multiplayer, networking/replication, AGR wallet/payments, marketplace/economy transfer logic, auth, security, migrations, deployment secrets, and broad refactors. Return JSON only.'
|
|
|
|
user_prompt=$(cat <<EOF
|
|
Task:
|
|
${TASK}
|
|
|
|
Repo evidence gathered by wrapper before any edits:
|
|
${repo_evidence}
|
|
|
|
Return compact JSON only with these keys:
|
|
risk: low|medium|high
|
|
confidence: number from 0.0 to 1.0
|
|
evidence_checked: array of strings
|
|
reason: short string
|
|
recommended_escalation: none|codex|human
|
|
requested_codex_action: short string
|
|
EOF
|
|
)
|
|
|
|
payload="$(
|
|
python3 - "$MODEL" "$system_prompt" "$user_prompt" <<'PY'
|
|
import json
|
|
import sys
|
|
model, system_prompt, user_prompt = sys.argv[1:4]
|
|
print(json.dumps({
|
|
"model": model,
|
|
"messages": [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": user_prompt},
|
|
],
|
|
"options": {
|
|
"num_ctx": 4096,
|
|
"num_predict": 220,
|
|
"temperature": 0.1
|
|
},
|
|
"stream": False,
|
|
}))
|
|
PY
|
|
)"
|
|
|
|
echo "LinaAI preflight with ${MODEL}..."
|
|
response="$(curl -fsS --max-time "${LINAAI_PREFLIGHT_TIMEOUT:-120}" "${OLLAMA_URL}/api/chat" -H "Content-Type: application/json" -d "$payload")"
|
|
content="$(printf '%s' "$response" | python3 -c 'import json,sys; print(json.load(sys.stdin)["message"]["content"])')"
|
|
|
|
python3 - "$content" "$PREFLIGHT_JSON" <<'PY'
|
|
import json
|
|
import re
|
|
import sys
|
|
content, output = sys.argv[1:3]
|
|
match = re.search(r"\{.*\}", content, re.S)
|
|
if not match:
|
|
raise SystemExit(f"No JSON object found in preflight response: {content}")
|
|
data = json.loads(match.group(0))
|
|
with open(output, "w", encoding="utf-8") as f:
|
|
json.dump(data, f, indent=2)
|
|
f.write("\n")
|
|
PY
|
|
|
|
risk="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("risk","high"))' "$PREFLIGHT_JSON")"
|
|
confidence="$(python3 -c 'import json,sys; print(float(json.load(open(sys.argv[1])).get("confidence",0)))' "$PREFLIGHT_JSON")"
|
|
recommended="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("recommended_escalation","codex"))' "$PREFLIGHT_JSON")"
|
|
reason="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("reason",""))' "$PREFLIGHT_JSON")"
|
|
evidence_count="$(python3 -c 'import json,sys; v=json.load(open(sys.argv[1])).get("evidence_checked",[]); print(len(v) if isinstance(v,list) else 0)' "$PREFLIGHT_JSON")"
|
|
if [[ "$evidence_count" -lt 2 ]]; then
|
|
confidence="$(python3 - "$confidence" <<'PY'
|
|
import sys
|
|
print(min(float(sys.argv[1]), 0.64))
|
|
PY
|
|
)"
|
|
fi
|
|
|
|
high_risk_regex='(save/load|save system|multiplayer|replication|networking|\bAGR\b|wallet|payment|marketplace|auth|security|migration|core architecture|engine source|broad refactor|private key|secret)'
|
|
keyword_escalate=0
|
|
if printf '%s' "$TASK" | grep -Eiq "$high_risk_regex"; then
|
|
keyword_escalate=1
|
|
fi
|
|
|
|
should_escalate=0
|
|
escalation_reason=""
|
|
if [[ "$FORCE_ESCALATE" -eq 1 ]]; then
|
|
should_escalate=1
|
|
escalation_reason="forced escalation test"
|
|
elif [[ "$risk" == "high" ]]; then
|
|
should_escalate=1
|
|
escalation_reason="high risk task"
|
|
elif [[ "$recommended" != "none" ]]; then
|
|
should_escalate=1
|
|
escalation_reason="local preflight recommended ${recommended}"
|
|
elif [[ "$keyword_escalate" -eq 1 ]]; then
|
|
should_escalate=1
|
|
escalation_reason="task matched high-risk escalation keywords"
|
|
else
|
|
below_threshold="$(python3 - "$confidence" "$THRESHOLD" <<'PY'
|
|
import sys
|
|
print("1" if float(sys.argv[1]) < float(sys.argv[2]) else "0")
|
|
PY
|
|
)"
|
|
if [[ "$below_threshold" == "1" ]]; then
|
|
should_escalate=1
|
|
escalation_reason="confidence ${confidence} below threshold ${THRESHOLD}"
|
|
fi
|
|
fi
|
|
|
|
write_status() {
|
|
local tests_passed="$1"
|
|
local build_passed="$2"
|
|
local blocked_reason="$3"
|
|
local requested_action="$4"
|
|
python3 - "$STATUS_JSON" "$TASK" "$current_branch" "$risk" "$confidence" "$tests_passed" "$build_passed" "$blocked_reason" "$requested_action" <<'PY'
|
|
import json
|
|
import sys
|
|
path, task, branch, risk, confidence, tests_passed, build_passed, blocked_reason, requested_action = sys.argv[1:10]
|
|
data = {
|
|
"task": task,
|
|
"project": "AgrarianGame",
|
|
"branch": branch,
|
|
"risk": risk,
|
|
"confidence": float(confidence),
|
|
"attempts": 1,
|
|
"files_inspected": [],
|
|
"files_changed": [],
|
|
"commands_run": [],
|
|
"tests_passed": tests_passed == "true",
|
|
"build_passed": build_passed == "true",
|
|
"blocked_reason": blocked_reason,
|
|
"recommended_escalation": "codex",
|
|
"requested_codex_action": requested_action,
|
|
}
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
json.dump(data, f, indent=2)
|
|
f.write("\n")
|
|
PY
|
|
}
|
|
|
|
echo "Risk: ${risk}"
|
|
echo "Confidence: ${confidence}"
|
|
echo "Evidence entries: ${evidence_count}"
|
|
echo "Reason: ${reason}"
|
|
echo "Preflight: ${PREFLIGHT_JSON}"
|
|
|
|
if [[ "$should_escalate" -eq 1 ]]; then
|
|
echo "Routing to Codex: ${escalation_reason}"
|
|
write_status false false "$escalation_reason" "Handle this task or provide a precise implementation plan. Do not edit files unless the task explicitly requires edits."
|
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
echo "Dry run: would run Scripts/ai_codex_escalate.sh ${STATUS_JSON}"
|
|
exit 0
|
|
fi
|
|
exec Scripts/ai_codex_escalate.sh "$STATUS_JSON"
|
|
fi
|
|
|
|
if [[ -x Scripts/linaai_bootstrap_context.sh && ! -f Saved/LinaAIKnowledge/context.md ]]; then
|
|
echo "Building LinaAI bootstrap context..."
|
|
Scripts/linaai_bootstrap_context.sh >/dev/null || true
|
|
fi
|
|
|
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
echo "Dry run: would run Aider locally."
|
|
exit 0
|
|
fi
|
|
|
|
echo "Routing to Aider local execution."
|
|
aider_read_args=()
|
|
for read_doc in \
|
|
Docs/AI/LinaAIOperatingManual.md \
|
|
Docs/AI/LocalAgentGuardrails.md \
|
|
Docs/AI/LinaAISecretsPolicy.md \
|
|
Docs/AI/LinaAIKnowledgeMap.md \
|
|
Saved/LinaAIKnowledge/context.md
|
|
do
|
|
if [[ -f "$read_doc" ]]; then
|
|
aider_read_args+=(--read "$read_doc")
|
|
fi
|
|
done
|
|
|
|
set +e
|
|
aider --model "ollama/${MODEL}" --no-auto-commits --yes-always "${aider_read_args[@]}" --message "$TASK"
|
|
aider_exit=$?
|
|
set -e
|
|
|
|
if [[ "$aider_exit" -ne 0 ]]; then
|
|
echo "Aider failed with exit code ${aider_exit}; escalating to Codex."
|
|
write_status false false "Aider failed with exit code ${aider_exit}" "Review the task, Aider failure, and repository state; complete or advise next steps."
|
|
exec Scripts/ai_codex_escalate.sh "$STATUS_JSON"
|
|
fi
|
|
|
|
echo "Aider completed. Review git diff and run verification before committing."
|