agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
70 lines (69 loc) • 3.93 kB
TypeScript
/**
* withCredentialRetry — CredentialProvider decorator that retries transient
* `getCredential` failures with exponential backoff.
*
* Pattern: Decorator (GoF) — the credential twin of `withRetry` (resilience)
* for LLM providers. SAME option vocabulary (`maxAttempts` /
* `initialDelayMs` / `backoffFactor` / `maxDelayMs` / `shouldRetry` /
* `onRetry`) and the SAME default transience policy (the shared
* `defaultShouldRetry`: skip AbortError + 4xx except 429; retry 5xx,
* network errors, unknown shapes). AgentCore's documented transient
* errors (`InternalServerException` 500, `ThrottlingException` 429)
* retry out of the box.
*
* Why a decorator and not a reliability rule: the rules-based reliability
* subsystem (`Agent.create(...).reliability({...})`) is LLM-call-scoped —
* `ReliabilityScope` carries `request: LLMRequest` and the gate chart loops
* around the CallLLM stage with provider failover. Credential resolution
* happens at a different boundary (the tool-dispatch loop, declare-and-push);
* promoting it to a chart-level gate is the deferred `sf-credential` node.
* Until that exists, retry is a TRANSPORT property of the credential provider
* — exactly like `withRetry` is for LLM transports.
*
* Semantics:
* • Only THROWN errors retry. Both result branches return immediately:
* `issued` is success; `authorization-required` is a HUMAN flow (3LO
* consent), not a transient fault — retrying it would hammer the IdP
* without the user having authorized anything.
* • The request's `signal` (`CredentialRequest.signal`) is passed through to
* the wrapped provider, ends the backoff wait, and is checked immediately
* before every attempt, the first included: a request whose signal has
* fired never asks the provider (again), and the call rejects with the
* abort. An attempt already running is the provider's to stop.
* • After retries exhaust, the LAST error is rethrown — the call site
* behaves byte-identically to an unwrapped provider (fail-closed:
* `agentfootprint.credential.failed` emit + error tool result; the tool
* does NOT run).
* • Visibility: the agent trace brackets the whole retried resolution with
* `credential.requested` → `credential.acquired` / `credential.failed`.
* Per-attempt visibility is consumer-wired via `onRetry` — the established
* decorator contract (same as `withRetry`; the `agentfootprint.error.*`
* event family is reserved for these decorators, see events/payloads.ts).
* No new event types.
*
* @example
* import { agentCoreIdentity, withCredentialRetry } from 'agentfootprint/security';
*
* const credentials = withCredentialRetry(agentCoreIdentity({ region: 'us-east-1' }), {
* maxAttempts: 3,
* onRetry: (err, attempt, ms) => console.warn(`credential retry ${attempt} in ${ms}ms`, err),
* });
* const agent = Agent.create({ provider, model, credentials }).tools([...]).build();
*/
import { type WithRetryOptions } from '../resilience/withRetry.js';
import type { CredentialProvider } from './types.js';
/**
* Same vocabulary as the LLM-provider `withRetry` — one retry language across
* both decorators. `shouldRetry` sees only THROWN errors (never an
* `authorization-required` result, which returns immediately).
*/
export type WithCredentialRetryOptions = WithRetryOptions;
/**
* Wrap a {@link CredentialProvider} so transient `getCredential` failures
* retry with exponential backoff before failing closed.
*
* Defaults mirror `withRetry`: 3 attempts total, 200ms → 400ms → 800ms
* backoff capped at 10s, retry on 5xx/429/network/unknown, never on
* AbortError or other 4xx.
*/
export declare function withCredentialRetry(provider: CredentialProvider, options?: WithCredentialRetryOptions): CredentialProvider;