Add LinaAI automatic Codex routing

This commit is contained in:
2026-05-24 07:24:58 +00:00
parent e82045a7f9
commit 2d3e0454cd
5 changed files with 330 additions and 15 deletions
+30 -6
View File
@@ -21,6 +21,7 @@ mkdir -p "$OUT_DIR"
PROMPT_FILE="${OUT_DIR}/codex_prompt.txt"
LOG_FILE="${OUT_DIR}/codex_exec.log"
BYPASS_LOG_FILE="${OUT_DIR}/codex_exec_bypass.log"
{
echo "You are Codex being called as an escalation worker for Agrarian."
@@ -41,10 +42,33 @@ LOG_FILE="${OUT_DIR}/codex_exec.log"
echo "Prompt written to ${PROMPT_FILE}"
if command -v codex >/dev/null 2>&1; then
codex exec "$(cat "$PROMPT_FILE")" 2>&1 | tee "$LOG_FILE"
else
npx -y @openai/codex exec "$(cat "$PROMPT_FILE")" 2>&1 | tee "$LOG_FILE"
fi
run_codex_sandboxed() {
if command -v codex >/dev/null 2>&1; then
codex exec --sandbox workspace-write -C "$ROOT" - < "$PROMPT_FILE" 2>&1 | tee "$LOG_FILE"
else
npx -y @openai/codex exec --sandbox workspace-write -C "$ROOT" - < "$PROMPT_FILE" 2>&1 | tee "$LOG_FILE"
fi
}
echo "Codex escalation log written to ${LOG_FILE}"
run_codex_bypass() {
{
echo "LinaAI note: Codex sandbox failed inside the isolated LinaAI VM."
echo "Retrying with Codex sandbox bypass so escalation can inspect/run commands."
echo "This should only be used from LinaAI, not shared production hosts."
echo
if command -v codex >/dev/null 2>&1; then
codex exec --dangerously-bypass-approvals-and-sandbox -C "$ROOT" - < "$PROMPT_FILE"
else
npx -y @openai/codex exec --dangerously-bypass-approvals-and-sandbox -C "$ROOT" - < "$PROMPT_FILE"
fi
} 2>&1 | tee "$BYPASS_LOG_FILE"
}
run_codex_sandboxed
if grep -q "bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted" "$LOG_FILE"; then
run_codex_bypass
echo "Codex escalation bypass log written to ${BYPASS_LOG_FILE}"
else
echo "Codex escalation log written to ${LOG_FILE}"
fi
+258
View File
@@ -0,0 +1,258 @@
#!/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 -40
echo "doc_samples:"
find Docs -maxdepth 2 -type f -printf "%p\n" 2>/dev/null | sort | head -40
} | sed 's/"/'\''/g'
)"
system_prompt='You are LinaAI, a supervised local coding assistant for Agrarian. 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. 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},
],
"stream": False,
}))
PY
)"
echo "LinaAI preflight with ${MODEL}..."
response="$(curl -fsS "${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|AGR|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 [[ "$DRY_RUN" -eq 1 ]]; then
echo "Dry run: would run Aider locally."
exit 0
fi
echo "Routing to Aider local execution."
set +e
aider --model "ollama/${MODEL}" --no-auto-commits --yes-always --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."