Files
xh1-research-fork/xh1-research.broken.20260825-200733
2026-08-25 20:22:11 +02:00

288 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# XH-1 Research Harness
# Autonomous Research / Review / Revision Pipeline
#
# Pure Bash
# OpenAI-compatible APIs
# Designed for OpenRouter
# ============================================================
ROOT="${XH1_RESEARCH_ROOT:-research}"
CONFIG="${XH1_RESEARCH_CONFIG:-xh1-research.conf}"
STATE="${ROOT}/.xh1"
mkdir -p \
"$STATE/logs" \
"$STATE/responses" \
"$STATE/runs"
# ------------------------------------------------------------
# Defaults
# ------------------------------------------------------------
API_BASE_URL="${API_BASE_URL:-https://openrouter.ai/api/v1}"
API_KEY_ENV="${API_KEY_ENV:-OPENROUTER_API_KEY}"
RESEARCH_MODEL="${RESEARCH_MODEL:-}"
REVIEW_MODEL="${REVIEW_MODEL:-$RESEARCH_MODEL}"
REVISION_MODEL="${REVISION_MODEL:-$RESEARCH_MODEL}"
MAX_RESEARCH_ROUNDS="${MAX_RESEARCH_ROUNDS:-3}"
MAX_API_RETRIES="${MAX_API_RETRIES:-3}"
DELAY_SECONDS="${DELAY_SECONDS:-5}"
MAX_ITERATIONS="${MAX_ITERATIONS:-0}"
REVIEW_ENABLED="${REVIEW_ENABLED:-true}"
AUTO_COMMIT="${AUTO_COMMIT:-false}"
RUN_ID=""
# ------------------------------------------------------------
# General
# ------------------------------------------------------------
die() {
echo "ERROR: $*" >&2
exit 1
}
log() {
local message="$*"
echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $message" \
| tee -a "$STATE/logs/harness.log"
}
require() {
command -v "$1" >/dev/null 2>&1 ||
die "Required command not found: $1"
}
require_dependencies() {
require bash
require curl
require jq
require find
require grep
require sed
require awk
}
timestamp() {
date -u '+%Y%m%dT%H%M%SZ'
}
load_config() {
if [[ -f "$CONFIG" ]]; then
# shellcheck disable=SC1090
source "$CONFIG"
fi
}
get_api_key() {
local key="${!API_KEY_ENV:-}"
[[ -n "$key" ]] ||
die "Missing API key. Set ${API_KEY_ENV}."
printf '%s' "$key"
}
# ------------------------------------------------------------
# Configuration
# ------------------------------------------------------------
init_config() {
if [[ -f "$CONFIG" ]]; then
echo "Configuration already exists:"
echo " $CONFIG"
return
fi
cat > "$CONFIG" <<'CONFIG'
# ============================================================
# XH-1 Research Harness Configuration
# ============================================================
# OpenAI-compatible API endpoint.
# OpenRouter:
API_BASE_URL="https://openrouter.ai/api/v1"
# Environment variable containing the API key.
API_KEY_ENV="OPENROUTER_API_KEY"
# ------------------------------------------------------------
# Models
# ------------------------------------------------------------
# Model used for initial research.
RESEARCH_MODEL=""
# Model used for independent review.
REVIEW_MODEL=""
# Model used to revise rejected research.
REVISION_MODEL=""
# ------------------------------------------------------------
# Research behavior
# ------------------------------------------------------------
# Maximum Research -> Review -> Revision cycles for ONE topic.
MAX_RESEARCH_ROUNDS=3
# API retries per individual request.
MAX_API_RETRIES=3
# Delay between topics.
DELAY_SECONDS=5
# 0 = unlimited.
MAX_ITERATIONS=0
# Enable independent reviewer.
REVIEW_ENABLED=true
# Automatically create git commits.
AUTO_COMMIT=false
CONFIG
echo "Created $CONFIG"
}
# ------------------------------------------------------------
# Run state
# ------------------------------------------------------------
start_run() {
RUN_ID="$(timestamp)"
mkdir -p "$STATE/runs/$RUN_ID"
: > "$STATE/runs/$RUN_ID/completed"
: > "$STATE/runs/$RUN_ID/failed"
: > "$STATE/runs/$RUN_ID/attempts"
log "Started run: $RUN_ID"
}
run_has_completed() {
local file="$1"
grep -Fxq "$file" \
"$STATE/runs/$RUN_ID/completed" 2>/dev/null
}
run_has_failed() {
local file="$1"
grep -Fxq "$file" \
"$STATE/runs/$RUN_ID/failed" 2>/dev/null
}
mark_completed() {
local file="$1"
printf '%s\n' "$file" \
>> "$STATE/runs/$RUN_ID/completed"
}
mark_failed() {
local file="$1"
printf '%s\n' "$file" \
>> "$STATE/runs/$RUN_ID/failed"
}
record_attempt() {
local file="$1"
local round="$2"
local phase="$3"
local result="$4"
printf '%s\t%s\t%s\t%s\t%s\n' \
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
"$file" \
"$round" \
"$phase" \
"$result" \
>> "$STATE/runs/$RUN_ID/attempts"
}
# ------------------------------------------------------------
# Research discovery
# ------------------------------------------------------------
is_soon() {
grep -qE '^SOON[[:space:]]*$' "$1"
}
find_next_task() {
while IFS= read -r -d '' file; do
[[ "$file" == "$STATE"/* ]] && continue
if is_soon "$file"; then
if ! run_has_completed "$file" &&
! run_has_failed "$file"
then
printf '%s\n' "$file"
return 0
fi
fi
done < <(
find "$ROOT" \
-type f \
-name '*.md' \
-not -path "$STATE/*" \
-print0
)
return 1
}
# ------------------------------------------------------------
# Context
# ------------------------------------------------------------
build_context() {
local file="$1"
local relative="${file#"$ROOT"/}"
local area="${relative%%/*}"
cat <<EOF
XH-1 RESEARCH PROJECT
=====================
Project:
XH-1
Architecture:
Custom 128-core RISC-V processor
Research repository:
XH-1 Research
Current document:
$file
Research area:
$area
CURRENT DOCUMENT
================