Skip to content

Aeon

Aeon #38

Workflow file for this run

name: Aeon
"on":
workflow_dispatch:
inputs:
skill:
description: Skill name
required: false
default: digest
var:
description: Skill var
required: false
default: ""
model:
description: Model override
required: false
default: ""
charon_approval:
description: Charon approval id
required: false
default: ""
jobs:
run:
runs-on: ubuntu-latest
permissions:
contents: write
actions: write
steps:
- uses: actions/checkout@v5
- id: work
run: echo "mode=skill" >> "$GITHUB_OUTPUT"
- id: skill
run: echo "name=${{ inputs.skill || 'digest' }}" >> "$GITHUB_OUTPUT"
- name: Charon approval
if: inputs.charon_approval != ''
run: |
{
echo "### Charon approval"
echo ""
echo "- review: \`${{ inputs.charon_approval }}\`"
echo "- preflight: \`approved by Telegram callback\`"
} >> "$GITHUB_STEP_SUMMARY"
# >>> charon aeon preflight
- name: Charon preflight
id: charon_preflight
if: steps.work.outputs.mode != '' && inputs.charon_approval == ''
env:
SKILL: ${{ steps.skill.outputs.name }}
SKILL_VAR: ${{ inputs.var }}
AEON_ACTOR: ${{ github.actor }}
AEON_TRIGGER: ${{ github.event_name == 'repository_dispatch' && github.event.action || github.event_name }}
run: |
set +e
mkdir -p .charon/aeon
npx -y github:CharonAI-code/charon aeon preflight \
--skill "$SKILL" \
--var "$SKILL_VAR" \
--trigger "$AEON_TRIGGER" \
--repo "$GITHUB_REPOSITORY" \
--run-id "$GITHUB_RUN_ID" \
--actor "$AEON_ACTOR" \
--policy charon.aeon.yml \
--review > .charon/aeon/preflight.json
code=$?
cat .charon/aeon/preflight.json
if [ "$code" = "125" ]; then
npx -y github:CharonAI-code/charon aeon telegram send latest || true
elif [ "$code" = "126" ]; then
npx -y github:CharonAI-code/charon aeon telegram send --preflight .charon/aeon/preflight.json || true
fi
exit "$code"
- name: Charon review export
if: always()
id: charon_review
run: |
npx -y github:CharonAI-code/charon aeon review export latest --github-output "$GITHUB_OUTPUT" || true
for file in .charon/aeon/exports/*.md; do
if [ -f "$file" ]; then cat "$file" >> "$GITHUB_STEP_SUMMARY"; fi
done
# <<< charon aeon preflight
- name: Run pre-fetch scripts
run: echo "prefetch"
- name: Run
id: aeon_run
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
ANTHROPIC_BASE_URL: ${{ vars.ANTHROPIC_BASE_URL }}
BANKR_LLM_KEY: ${{ secrets.BANKR_LLM_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
USEPOD_TOKEN: ${{ secrets.USEPOD_TOKEN }}
VENICE_API_KEY: ${{ secrets.VENICE_API_KEY }}
SURPLUS_API_KEY: ${{ secrets.SURPLUS_API_KEY }}
OPENROUTER_MODEL: ${{ vars.OPENROUTER_MODEL }}
OPENROUTER_MODEL_SONNET: ${{ vars.OPENROUTER_MODEL_SONNET }}
OPENROUTER_MODEL_HAIKU: ${{ vars.OPENROUTER_MODEL_HAIKU }}
USEPOD_MODEL: ${{ vars.USEPOD_MODEL }}
USEPOD_MODEL_SONNET: ${{ vars.USEPOD_MODEL_SONNET }}
USEPOD_MODEL_HAIKU: ${{ vars.USEPOD_MODEL_HAIKU }}
VENICE_MODEL: ${{ vars.VENICE_MODEL }}
VENICE_BASE_URL: ${{ vars.VENICE_BASE_URL }}
VENICE_CLEANCACHE: ${{ vars.VENICE_CLEANCACHE }}
SURPLUS_MODEL: ${{ vars.SURPLUS_MODEL }}
CCR_LOG: ${{ vars.CCR_LOG }}
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
INPUT_MODEL: ${{ inputs.model }}
SKILL_NAME: ${{ steps.skill.outputs.name }}
SKILL_VAR: ${{ inputs.var }}
run: |
if ! command -v claude >/dev/null 2>&1; then
npm install -g @anthropic-ai/claude-code@2.1.168 >/dev/null
fi
CONFIG_MODEL="$(grep -E '^model:' aeon.yml | sed 's/^model: *//' | tr -d ' \"')"
SKILL_MODEL="$(grep "^ ${SKILL_NAME}:" aeon.yml | sed -n 's/.*model: *\"\([^\"]*\)\".*/\1/p')"
if [ -n "$INPUT_MODEL" ] && [ "$INPUT_MODEL" != "(config default)" ]; then
MODEL="$INPUT_MODEL"
elif [ -n "$SKILL_MODEL" ]; then
MODEL="$SKILL_MODEL"
else
MODEL="${CONFIG_MODEL:-claude-opus-4-8}"
fi
echo "Using model: $MODEL"
echo "SKILL_MODEL=$MODEL" >> "$GITHUB_OUTPUT"
BASE_MODEL="$MODEL"
GW="${GITHUB_WORKSPACE}/scripts/llm-gateway.sh"
GATEWAY=$(grep -A1 '^gateway:' aeon.yml | grep 'provider:' | sed 's/.*provider:[[:space:]]*//' | sed "s/[\"' ]//g" || true)
GATEWAY="${GATEWAY:-auto}"
if [ "$GATEWAY" = "auto" ]; then
CANDIDATES=$(AEON_LIST_CANDIDATES=1 bash "$GW")
else
CANDIDATES="$GATEWAY"
fi
echo "Gateway: $GATEWAY | cascade order: $CANDIDATES"
cat > ./notify <<'NOTIFY'
#!/usr/bin/env bash
set -euo pipefail
msg="$1"
touch .aeon-notified
if [ -n "${TELEGRAM_BOT_TOKEN:-}" ] && [ -n "${TELEGRAM_CHAT_ID:-}" ]; then
curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
--data-urlencode "text=${msg}" \
-d "disable_web_page_preview=true" >/dev/null || true
fi
NOTIFY
chmod +x ./notify
ALLOWED="Bash(git:*),Bash(./notify:*),Write,Edit"
case "$SKILL_NAME" in
digest|security-digest|hn-digest|github-trending|framework-watch|mcp-pulse)
ALLOWED="$ALLOWED,WebSearch,WebFetch"
;;
esac
echo "Allowed tools: $ALLOWED"
prompt="$(printf 'Run AEON skill: %s\n\nTask:\n%s\n\nWhen finished, send the user a concise result through ./notify. Include what changed or what happened. Do not only say complete.' "$SKILL_NAME" "$SKILL_VAR")"
CLAUDE_OK=0
USED_GATEWAY=""
for cand in $CANDIDATES; do
echo "::notice::Routing attempt via '$cand'"
if CLAUDE_OUTPUT=$(
GATEWAY="$cand"
MODEL="$BASE_MODEL"
source "$GW" >&2
echo "$prompt" | claude -p - --model "$MODEL" --allowedTools "$ALLOWED" --output-format json 2>&1
); then
USED_GATEWAY="$cand"
CLAUDE_OK=1
break
fi
echo "::warning::provider '$cand' failed - $(printf '%s' "$CLAUDE_OUTPUT" | tail -c 160 | tr '\n' ' ')"
done
if [ "$CLAUDE_OK" != "1" ]; then
echo "::error::All gateway providers failed ($CANDIDATES). Last output: $CLAUDE_OUTPUT"
exit 1
fi
echo "GATEWAY=$USED_GATEWAY" >> "$GITHUB_OUTPUT"
RESULT_TEXT="$(echo "$CLAUDE_OUTPUT" | jq -r '.result // empty')"
printf '%s\n' "$RESULT_TEXT"
if [ ! -f .aeon-notified ]; then
summary="$(printf '%s' "$RESULT_TEXT" | tail -c 3000)"
./notify "$(printf 'AEON result\n\n%s' "$summary")" || true
fi