agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
76 lines • 3.56 kB
TypeScript
/**
* CircuitBreaker — pure state-machine functions for the Nygard breaker
* pattern.
*
* Refactored from a class-with-instance-state to PURE FUNCTIONS that
* take a state record and return a new one. Reasons:
*
* 1. **No hidden runtime state.** Breaker state lives in scope where
* it's visible to commitLog, narrative, and rules — the footprintjs
* "everything in scope" principle. The closure used to be the
* source of truth and scope held only a projection; now scope IS
* the source of truth.
*
* 2. **Round-trippable across gate invocations.** Because state is a
* plain record, gate's outputMapper writes it back to agent scope;
* agent scope persists across the ReAct loop's many LLM-call gate
* invocations; gate's inputMapper reads it back in for the next
* call. Per-process persistence comes from the agent scope, not
* from a closure that hides between runs.
*
* 3. **Distributable later.** A future v2.12 `BreakerStateStore`
* adapter (Redis/DynamoDB) just needs to serialize/deserialize the
* state record. No class instances to reconstruct.
*
* 4. **Testable in isolation.** Pure functions; no instance setup.
*
* Pattern: Nygard *Release It!* — three states (CLOSED → OPEN →
* HALF-OPEN) with cooldown and probe-success thresholds.
*/
import type { CircuitBreakerConfig } from './types.js';
export type CircuitState = 'closed' | 'open' | 'half-open';
/** Plain serializable record holding one breaker's full state. */
export interface BreakerState {
state: CircuitState;
consecutiveFailures: number;
consecutiveSuccesses: number;
openedAt: number;
lastErrorMessage?: string;
}
/**
* Thrown by `assertAdmit()` when the breaker is OPEN and the cooldown
* window has not elapsed. The reliability gate stage catches this,
* classifies via `classifyError` → `'circuit-open'`, and lets the
* post-decide rules route on it.
*/
export declare class CircuitOpenError extends Error {
readonly code: "ERR_CIRCUIT_OPEN";
readonly cause: unknown;
readonly retryAfter: number;
constructor(providerName: string, lastErrorMessage: string | undefined, retryAfter: number);
}
/** Initial state for a freshly-CLOSED breaker. */
export declare function initialBreakerState(): BreakerState;
/**
* Decide whether to admit a call. Returns the (possibly-updated) state
* AND whether to admit. If OPEN and cooldown elapsed, transitions to
* HALF-OPEN and admits. Pure: caller must use the returned state.
*
* Usage in the gate stage:
* ```ts
* const { admitted, nextState } = admitCall(scope.breakerStates[name], config);
* scope.breakerStates[name] = nextState;
* if (!admitted) throw new CircuitOpenError(name, nextState.lastErrorMessage, ...);
* ```
*/
export declare function admitCall(state: BreakerState, config: CircuitBreakerConfig | undefined): {
admitted: boolean;
nextState: BreakerState;
};
/** Record a successful call. Returns the (possibly-updated) state. */
export declare function recordSuccess(state: BreakerState, config: CircuitBreakerConfig | undefined): BreakerState;
/** Record a failed call. Returns the (possibly-updated) state. */
export declare function recordFailure(state: BreakerState, err: unknown, config: CircuitBreakerConfig | undefined): BreakerState;
/** Compute the next probe time given a state + config. */
export declare function nextProbeTime(state: BreakerState, config: CircuitBreakerConfig | undefined): number;
//# sourceMappingURL=CircuitBreaker.d.ts.map