From 415d75c97f2f061fd2cb2ff24e626def689d59f5 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Tue, 28 Apr 2026 18:47:57 +0200 Subject: [PATCH] freeloader: exhaust full model pool before giving up; fix resubmission and stream newlines - scripts/x/freeloader: track tried models per-session in freeloader-tried-$SID; walk the full sorted :free pool in order before exiting 1; write recovery prompt to fifo.in (not prompt) to avoid goroutine race - core submodule: 404 tool_unsupported detection + FIFO drain on error - tui submodule: fix mid-stream newlines from backgroundTail chatOff race Co-Authored-By: Claude Sonnet 4.6 --- core | 2 +- mkfile | 3 ++- scripts/x/freeloader | 53 ++++++++++++++++++++++++++++---------------- tui | 2 +- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/core b/core index d350c9f..c812f08 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit d350c9f4125108605e1603fa678e11e98f3ecfab +Subproject commit c812f0882ee4bc50f060d9af403b61cb1d0d9966 diff --git a/mkfile b/mkfile index 6d55f58..6ccd27f 100755 --- a/mkfile +++ b/mkfile @@ -62,7 +62,8 @@ scripts:V: cp -f scripts/x/bd $HOME/.config/ollie/scripts/x/bd cp -f scripts/x/prime $HOME/.config/ollie/scripts/x/prime cp -f scripts/x/readmulti $HOME/.config/ollie/scripts/x/readmulti - chmod 755 $HOME/.config/ollie/scripts/s/b $HOME/.config/ollie/scripts/s/bfg $HOME/.config/ollie/scripts/s/bbg $HOME/.config/ollie/scripts/s/cleanup $HOME/.config/ollie/scripts/u/optimize $HOME/.config/ollie/scripts/x/elevate $HOME/.config/ollie/scripts/x/elevate-adapter-superpowerd $HOME/.config/ollie/scripts/x/elevate-adapter-start $HOME/.config/ollie/scripts/x/bd $HOME/.config/ollie/scripts/x/prime $HOME/.config/ollie/scripts/x/readmulti + cp -f scripts/x/freeloader $HOME/.config/ollie/scripts/x/freeloader + chmod 755 $HOME/.config/ollie/scripts/s/b $HOME/.config/ollie/scripts/s/bfg $HOME/.config/ollie/scripts/s/bbg $HOME/.config/ollie/scripts/s/cleanup $HOME/.config/ollie/scripts/u/optimize $HOME/.config/ollie/scripts/x/elevate $HOME/.config/ollie/scripts/x/elevate-adapter-superpowerd $HOME/.config/ollie/scripts/x/elevate-adapter-start $HOME/.config/ollie/scripts/x/bd $HOME/.config/ollie/scripts/x/prime $HOME/.config/ollie/scripts/x/readmulti $HOME/.config/ollie/scripts/x/freeloader help:V: echo 'Ollie monorepo build targets:' diff --git a/scripts/x/freeloader b/scripts/x/freeloader index e33df03..264513a 100755 --- a/scripts/x/freeloader +++ b/scripts/x/freeloader @@ -1,23 +1,23 @@ #!/usr/bin/env bash # x/freeloader: turnError hook handler for free-model sessions on OpenRouter. # -# Intercepts rate_limit and tool_unsupported errors on :free models, switches -# to the next viable free model, and resubmits the previous prompt. +# Intercepts rate_limit and tool_unsupported errors on :free models, walks +# through every viable free model once per round, and resubmits the previous +# prompt on each switch. When the pool is exhausted, clears the tried list +# and exits 1 so normal error handling takes over. # # Configure in an agent .json: # { "hooks": { "turnError": "$OLLIE/x/freeloader $OLLIE_SESSION_ID" } } # -# The hook runner sets OLLIE_ERROR_TYPE, OLLIE_MODEL, and OLLIE_SESSION_ID -# from the payload before executing this script. -# # Exit codes: -# 0 — error was intercepted and handled (loop will not retry) -# 1 — error not handled; normal retry/error behaviour proceeds +# 0 — error intercepted, switched model and resubmitted +# 1 — pool exhausted or error not applicable; normal handling proceeds set -euo pipefail OLLIE="${OLLIE:-$HOME/mnt/ollie}" -BLACKLIST="${XDG_CONFIG_HOME:-$HOME/.config}/ollie/freeloader-blacklist" +CFG="${XDG_CONFIG_HOME:-$HOME/.config}/ollie" +BLACKLIST="$CFG/freeloader-blacklist" cmd=${0##*/} die() { printf '%s: %s\n' "$cmd" "$*" >&2; exit 1; } @@ -29,7 +29,6 @@ B="$OLLIE/s/$SID" [ -d "$B" ] || die "session not found: $SID" -# Read context injected by the hook runner. error_type="${OLLIE_ERROR_TYPE:-}" current_model="${OLLIE_MODEL:-$(grep '^model=' "$B/cfg" 2>/dev/null | cut -d= -f2-)}" @@ -45,36 +44,52 @@ case "$current_model" in *) exit 1 ;; esac -# For tool_unsupported: blacklist the current model so future sessions skip it. +# For tool_unsupported: permanently blacklist the current model. if [ "$error_type" = "tool_unsupported" ]; then - mkdir -p "$(dirname "$BLACKLIST")" + mkdir -p "$CFG" if ! grep -qxF "$current_model" "$BLACKLIST" 2>/dev/null; then printf '%s\n' "$current_model" >> "$BLACKLIST" printf '%s: blacklisted %s (tool_unsupported)\n' "$cmd" "$current_model" >&2 fi fi -# Build candidate list: :free models not in the blacklist, excluding current. -mapfile -t candidates < <( +# Per-session tried list: tracks models attempted in the current round. +TRIED="$CFG/freeloader-tried-$SID" +mkdir -p "$CFG" + +# Mark current model as tried. +if ! grep -qxF "$current_model" "$TRIED" 2>/dev/null; then + printf '%s\n' "$current_model" >> "$TRIED" +fi + +# Build the full sorted :free pool (blacklist excluded). +mapfile -t pool < <( cat "$B/models" 2>/dev/null \ | grep ':free' \ | sort \ - | grep -vxF "$current_model" \ | { [ -f "$BLACKLIST" ] && grep -vxFf "$BLACKLIST" || cat; } ) -if [ ${#candidates[@]} -eq 0 ]; then - printf '%s: no viable free models remaining\n' "$cmd" >&2 +# Find first model not yet tried this round. +next_model="" +for m in "${pool[@]}"; do + if ! grep -qxF "$m" "$TRIED" 2>/dev/null; then + next_model="$m" + break + fi +done + +if [ -z "$next_model" ]; then + printf '%s: all free models exhausted, giving up\n' "$cmd" >&2 + rm -f "$TRIED" exit 1 fi -next_model="${candidates[0]}" - # Switch model and resubmit the previous prompt. printf 'model %s\n' "$next_model" > "$B/ctl" || die "failed to switch model" prev=$(cat "$B/prompt.prev" 2>/dev/null) || die "prompt.prev not available" [ -z "$prev" ] && die "prompt.prev is empty" -printf '%s\n' "$prev" > "$B/prompt" || die "failed to resubmit prompt" +printf '%s\n' "$prev" > "$B/fifo.in" || die "failed to resubmit prompt" printf '%s: %s → %s\n' "$cmd" "$current_model" "$next_model" >&2 exit 0 diff --git a/tui b/tui index 8340ab9..b8dde7d 160000 --- a/tui +++ b/tui @@ -1 +1 @@ -Subproject commit 8340ab9a1406bce2e3890588613398db38e3b520 +Subproject commit b8dde7d6b7f040a50b2308cd5a5721be47cb95b9