fix(agent): keep the tripping correction and explain the restart-bound exit

When the redirect cap trips, the correction that cancelled the final attempt is
still sitting in _pending_redirect; finalize_turn's clear_interrupt() would drop
it silently. Drain it into the steer slot so it rides result["pending_steer"] and
becomes the next user turn on every surface that already honours that key.

Both new exit reasons get a turn-completion explanation so the user sees why the
turn stopped instead of an empty reply.
This commit is contained in:
Teknium
2026-09-09 05:04:36 -07:00
parent b24a781b6f
commit 69c999f8a7
2 changed files with 15 additions and 0 deletions

View File

@@ -41,6 +41,16 @@ _EXIT_REASON_EXPLANATIONS: Dict[str, str] = {
"the request was interrupted mid-call before a reply was "
"received. Send `continue` to retry."
),
"redirect_restart_limit_exceeded": (
"the request was cancelled by a new correction on every attempt, "
"so the turn stopped instead of retrying forever. Your last "
"correction is queued as the next message."
),
"rebuilt_restart_limit_exceeded": (
"every provider in the fallback chain kept failing over, so the "
"turn stopped instead of retrying forever. Send `continue` or "
"switch provider."
),
"budget_exhausted": (
"the per-turn iteration/cost budget was exhausted before a "
"final answer. Send `continue` to keep going."

View File

@@ -423,6 +423,11 @@ def apply_retry_restarts(
"refunding the iteration budget indefinitely.",
max_retries,
)
# The correction that tripped the cap was never applied; hand it back as the
# next user turn (result["pending_steer"]) instead of losing it to clear_interrupt().
_unapplied = agent._drain_pending_redirect()
if _unapplied:
agent.steer(_unapplied)
return _verdict("break")
# Cancelled request produced no valid assistant item: reuse the same logical
# iteration after the outer loop appends partial context + correction.