9f6d0d2281
- Bump root AGENTS.md header: 2026-05-14 → 2026-05-15, commit5ffbe0e24→53a740636, release v4.1.1 → v4.1.2 - Update file counts: 2034 (1337+697) → 2041 (1340+701), LOC ~292k → ~294k - Fix STRUCTURE: openclaw lives at src/openclaw/ (not src/features/); list more accurate feature modules in the parenthetical - Clarify interactive_bash gate: tmux binary on PATH via isInteractiveBashEnabled() (not 'tmux enabled') - Fix docs/reference/features.md hook counts: Tool Guard 14→16, Total base 52→54, total with team-mode 59→61 - Bump 'Generated' date on all 43 subdir AGENTS.md files to 2026-05-15 - Preserve promptAsync injection cautions verbatim (per request)
4.4 KiB
4.4 KiB
src/hooks/runtime-fallback/ — Reactive Provider Error Recovery
Generated: 2026-05-15
OVERVIEW
32 files. Session Tier hook that reactively switches to fallback models when API providers return errors at runtime (429, 503, quota exhausted, cooldown signals). Distinct from model-fallback (which applies preemptively at chat.params).
RUNTIME-FALLBACK vs MODEL-FALLBACK
| Aspect | runtime-fallback | model-fallback |
|---|---|---|
| Trigger | Reactive — after error occurs | Proactive — at request time |
| Event | session.error, message.updated, session.status | chat.params |
| Config source | categories[].fallback_models, agents[].fallback_models |
AGENT_MODEL_REQUIREMENTS hardcoded chains |
| State | Per-session FallbackState + cooldown tracking | Module-global pendingModelFallbacks |
| Use case | Provider errors during execution | Pre-configured agent fallback chains |
They operate independently — no direct integration.
ERROR DETECTION
HTTP Status Codes (configurable)
Default retry codes: 429, 500, 502, 503, 504
Error Message Patterns (constants.ts)
/rate.?limit/i, /too.?many.?requests/i, /quota.*reset.*after/i,
/exhausted.*capacity/i, /all.*credentials.*for.*model/i,
/cool(?:ing)?.?down/i, /model.*not.*supported/i,
/service.?unavailable/i, /overloaded/i, /temporarily.?unavailable/i
Error Type Classification (error-classifier.ts)
missing_api_key— provider rejects authmodel_not_found— model unavailablequota_exceeded— billing/quota hit- Auto-retry signal detection via
auto-retry-signal.ts— extracts "retrying in ~2 weeks" style signals, triggers immediate fallback
FALLBACK STATE MACHINE
interface FallbackState {
originalModel: string
currentModel: string
fallbackIndex: number
failedModels: Map<string, number> // model → cooldown-until timestamp
attemptCount: number
pendingFallbackModel?: string
}
FALLBACK CHAIN RESOLUTION (fallback-models.ts)
Priority order:
- Session category (via SessionCategoryRegistry)
- Agent config
fallback_models - Agent's category
fallback_models - Session ID pattern match (detect agent from session ID format)
RETRY FLOW
session.error / message.updated (with error) / session.status (retry signal)
→ isRetryableError(error)?
→ getFallbackModelsForSession(sessionID, agent)
→ findNextAvailableFallback() — skip cooldown models
→ prepareFallback() — update state, mark current failed
→ dispatchFallbackRetry() — toast notification + promptAsync with new model
→ 30s timeout — abort and try next if exceeded
COOLDOWN MECHANISM
Failed models enter 60s cooldown. findNextAvailableFallback() skips models in cooldown, preventing thrashing on persistently failing models.
KEY FILES
| File | Purpose |
|---|---|
hook.ts |
createRuntimeFallbackHook() — composes all handlers |
event-handler.ts |
Route session lifecycle (created, error, stop, idle) |
message-update-handler.ts |
Handle error parts in message.updated |
session-status-handler.ts |
Handle provider retry signals in session.status |
chat-message-handler.ts |
Apply fallback model override on chat.message |
error-classifier.ts |
isRetryableError(), classifyErrorType() |
auto-retry-signal.ts |
Extract "retrying in..." signals |
fallback-state.ts |
State machine: createFallbackState, prepareFallback, findNextAvailableFallback, isModelInCooldown |
fallback-models.ts |
Resolve chain from config hierarchy (strings + raw objects) |
fallback-bootstrap-model.ts |
Derive initial model when state missing |
fallback-retry-dispatcher.ts |
Toast + dispatch retry orchestration |
auto-retry.ts |
Abort, timeout scheduling, cleanup |
agent-resolver.ts |
Session → agent name normalization |
retry-model-payload.ts |
Build model payload (providerID/modelID/variant/reasoningEffort) |
visible-assistant-response.ts |
Detect if assistant produced real output vs just errors |
last-user-retry-parts.ts |
Extract last user message parts for retry |
NOTES
- Cooldown and failure tracking are per-session — concurrent sessions don't share state
visible-assistant-response.tsprevents retry if the assistant already produced a partial valid response- Runtime-fallback is registered in the Session Tier via
create-session-hooks.ts