Resilience
Retry, fallback, rate limiting, circuit breaking — production hardening for agent calls.
The agent loop is only as reliable as the calls underneath it. @agentoria/runtime ships composable primitives for the failure modes real apps hit.
Retry
Both providers already retry transient failures (429 / 5xx / network) with exponential backoff, jitter, and Retry-After. For your own fetch calls, resilientFetch does the same:
import { resilientFetch } from '@agentoria/runtime';
const res = await resilientFetch(url, init, { maxRetries: 2, retryBaseMs: 250 });Fallback
Re-route a single failed call to a backup provider:
import { withFallback } from '@agentoria/runtime';
const provider = withFallback(primary, backup); // tries each until one starts streamingCircuit breaker
Stop hammering a provider that's already down. After N consecutive failures it trips open and fails fast, then lets a single probe through to test recovery:
import { circuitBreaker } from '@agentoria/runtime';
const guarded = circuitBreaker((req) => provider.complete(req), {
failureThreshold: 5,
resetTimeoutMs: 30_000,
onStateChange: (from, to) => log(`circuit ${from} → ${to}`),
});Rate limiting
Cap concurrency and request rate — the layer between retry and a cost budget:
import { semaphore, tokenBucket, RateLimitedError } from '@agentoria/runtime';
const gate = semaphore(4); // ≤ 4 concurrent turns
const bucket = tokenBucket({ capacity: 20, refillPerSec: 5 }); // burst 20, then 5/s
if (!bucket.tryAcquire()) throw new RateLimitedError(bucket.timeUntil());
const reply = await gate.run(() => agent.run(req));semaphore is timer-free and FIFO-fair; tokenBucket is clock-injectable, so both are trivially testable.