/** * Observability hook contracts. * * Three optional hooks expose the substrate's state changes for * tracing, metrics, and dev logging: * * - `onDecision` — fires on every `Stipend.authorize` result. * Wired into `createProxyServer`. * - `onPaymentEvent` — fires across the payment lifecycle (intent, * approved, succeeded, failed, refunded) and * the receipt signing seam. Wired into * `createApiServer` (used by the Stripe webhook * handler and the /v1/payments routes). * - `onWorkerEvent` — fires across the outbound webhook delivery * worker (claimed, attempted, delivered, * failed, dead_lettered). Wired into * `createApiServer`. * * **Contract**: hooks are synchronous fire-and-forget from the * caller's perspective. Implementations MUST NOT throw — errors are * caught internally and warned to `console.warn` at most. Hooks are * observability, not control flow; their failure must not break a * payment or an authorization decision. * * **Zero-cost when unset**: callers that don't register a hook pay * nothing — the wiring is a single nullish check per event. * * **Stable wire shape**: hook payloads use domain types * (`AuthorizationContext`, `Payment`, `WebhookDelivery`) directly. * Subscribers should not need to JSON-encode anything; they project * into their own observability surface. */ import type { AuthorizationContext, AuthorizationDecision } from './authorization.js'; import type { Entitlement, Payment, Receipt } from './domain.js'; import type { WebhookDelivery } from './delivery.js'; export type DecisionHook = (decision: AuthorizationDecision, context: AuthorizationContext) => void; export type PaymentEventType = 'payment.created' | 'payment.intent_created' | 'payment.approved' | 'payment.succeeded' | 'payment.failed' | 'payment.refunded' | 'receipt.signed'; export interface PaymentEventContext { /** Stripe / provider event id, when this fires from an inbound webhook. */ readonly eventId?: string; /** The signed Receipt for `receipt.signed` events. */ readonly receipt?: Receipt; /** Entitlements granted by this payment, for `payment.succeeded` events. */ readonly entitlements?: readonly Entitlement[]; /** Free-form error message for `payment.failed` events. */ readonly errorMessage?: string; } export type PaymentEventHook = (type: PaymentEventType, payment: Payment, context?: PaymentEventContext) => void; export type WorkerEventType = 'delivery.claimed' | 'delivery.attempted' | 'delivery.delivered' | 'delivery.failed' | 'delivery.dead_lettered' /** * Fired when a worker tick observes a stale in-flight claim being * stolen back to `pending`. The store handles the actual transition * inside `claimDueDeliveries`; this event is the worker's signal to * subscribers that it happened. */ | 'delivery.released'; export interface WorkerEventContext { /** * Attempt number for this event. For `delivery.claimed` this is the * attempt that's about to be made (i.e. `delivery.attempts + 1`). */ readonly attempt?: number; /** HTTP status code, when the worker actually got a response. */ readonly statusCode?: number; /** Free-form error message for `delivery.failed` / `delivery.dead_lettered`. */ readonly errorMessage?: string; /** Scheduled time for the next attempt, on `delivery.failed` (retry path). */ readonly nextScheduledAt?: string; } export type WorkerEventHook = (type: WorkerEventType, delivery: WebhookDelivery, context?: WorkerEventContext) => void; /** * Optional hook fired when the proxy or API rejects a request for auth * reasons (missing/invalid/revoked bearer, paused account, bad API * key). Short human-readable reason; optional path on the API side. * Same fire-and-forget contract as the other hooks. * * Used by the dev CLI to surface "why isn't my agent working" hints in * real time; production hosts can wire it to their own log surface. */ export type AuthFailureHook = (reason: string, path?: string) => void; export interface ApiObservabilityHooks { readonly onPaymentEvent?: PaymentEventHook; readonly onWorkerEvent?: WorkerEventHook; /** See {@link AuthFailureHook}. */ readonly onAuthFailure?: AuthFailureHook; } /** * Invoke a hook, swallowing any thrown error with a single * `console.warn`. Hooks are observability, never control flow — a * broken subscriber must not break a payment or an authorize call. * * Use this at every call site rather than ad-hoc try/catch. Centralised * so the "errors don't propagate" contract is enforced uniformly. */ export declare function safeInvokeHook(label: string, hook: ((...args: Args) => void) | undefined, ...args: Args): void; //# sourceMappingURL=observability.d.ts.map