UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

94 lines • 4.75 kB
"use strict"; /** * resilienceHooks — turns a resilience decorator's `ResilienceReport` * into an already-declared typed event, emitted from inside the run. * * Pattern: Adapter (GoF) — one translation function, fact → event. * Role: The seam between `src/resilience/` (which knows nothing about * runs, scopes or events) and the in-run LLM call sites (which * hold a live scope). The decorator REPORTS; this maps the * report to `typedEmit`, so correlation ids come from * footprintjs's traversal rather than being synthesized. * Emits: agentfootprint.fallback.triggered * agentfootprint.error.retried * agentfootprint.error.recovered * agentfootprint.error.circuit_changed (9.32.0) * * Why here and not in the decorators: a decorator is constructed by the * consumer BEFORE any run exists, so it can never reach a scope. Passing * the hooks object per call is what lets the report cross that boundary. * * Every declared payload field is filled from a report field of the same * name — nothing is synthesized. `FallbackTriggeredPayload.kind` is the * literal `'provider'`; its `'tool'`/`'skill'` arms stay unused. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.resilienceHooks = void 0; const typedEmit_js_1 = require("./typedEmit.js"); const replayHooks_js_1 = require("./replayHooks.js"); /** * Build the per-call hooks object for one stage. Hoist it once per stage * rather than per attempt — it is stateless and safe to reuse across * reliability-loop iterations. * * @example * const providerHooks = resilienceHooks(scope); * const response = await provider.complete(request, providerHooks); */ function resilienceHooks(scope, receiptMinted = false, onReplayPersisted) { return { onReplayPrepared: (0, replayHooks_js_1.replayPreparedHook)(scope, receiptMinted, onReplayPersisted), onResilience(report) { // The library owns THIS sink, and it is the only one that runs // in-run — so guard it once here. Telemetry must never break an // LLM call (footprintjs's "recorder errors never abort traversal" // invariant). A CONSUMER-supplied onResilience is deliberately // unguarded, matching onRetry/onFallback/onStateChange. try { switch (report.kind) { case 'fell-back': (0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.fallback.triggered', { kind: 'provider', primary: report.primary, fallback: report.fallback, reason: report.reason, }); return; case 'retried': (0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.error.retried', { attempt: report.attempt, maxAttempts: report.maxAttempts, lastError: report.lastError, backoffMs: report.backoffMs, reason: report.reason, ...(report.statedWaitMs !== undefined && { statedWaitMs: report.statedWaitMs }), }); return; case 'recovered': (0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.error.recovered', { attempt: report.attempt, totalDurationMs: report.totalDurationMs, }); return; case 'circuit-changed': // 9.32.0 — the transition happened INSIDE this call, so the ids // footprintjs stamped before the stage ran are the right ones and // nothing here is synthesized. That is the whole reason the report // rides the per-call hooks rather than the breaker's own // `onStateChange`, which fires wherever the breaker lives. (0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.error.circuit_changed', { state: report.state, reason: report.reason, providerName: report.providerName, }); return; } } catch { // Swallowed by design — see the comment above. } }, }; } exports.resilienceHooks = resilienceHooks; //# sourceMappingURL=resilienceHooks.js.map