Skip to main content

Interceptors

An Interceptor is a composable hook around a run. The chain wraps the transport: each interceptor can observe or rewrite the outgoing request, then observe or transform the incoming event stream, before handing off to the next. This is how retry, auth, logging, and redaction are added without touching the transport or the agent.

import 'package:koel/koel.dart' show HttpAgent, KoelClient;
import 'package:koel_http/koel_http.dart'
show AuthInterceptor, RetryInterceptor;

final client = KoelClient(
agent: HttpAgent(url: Uri.parse('https://your-backend.example/agui')),
interceptors: [
RetryInterceptor(), // outermost: re-runs the whole call
AuthInterceptor(headers: () async => { // innermost: stamps headers
'Authorization': 'Bearer ${await tokenStore.read()}',
}),
],
);

Ordering: outermost wins

The list is applied in order, so the first interceptor is the outermost wrapper. A RetryInterceptor placed first re-runs everything inside it (including re-stamping auth on each attempt). A caller-supplied AuthInterceptor is prepended outermost of the auth pair so it wins the header merge over a bridge's default auth. When in doubt: read the order top-to-bottom as "outside-in".

The built-in interceptors (koel_http)

InterceptorWhat it does
RetryInterceptorRe-runs the call on transient transport failure, with backoff (RetryPolicy).
AuthInterceptorStamps headers from an async provider (headers: () async => {...}) — e.g. a Bearer token fetched per call.
LoggingInterceptorStructured request/response logging via package:logging (level:).
EventTraceInterceptorRecords each event into a TraceEntry sink: for devtools/debugging.
PIIRedactionInterceptorRedacts text matching configured patterns: before it reaches a log.
SentryBreadcrumbInterceptorEmits Sentry breadcrumbs for the run lifecycle (hub:).

The transport-layer interceptors live in koel_http; the chain framework (Interceptor) is koel_core, so a non-HTTP adapter reuses the same contract.

Four-stage pipeline

Under the interceptor chain, every event flows through koel_core's fixed four-stage pipeline — verify → apply → transform → chunk-synthesize. Only the chunk-synthesis stage (chunksStage, which normalizes the *_CHUNK shapes, see Events) is public, because a sibling transport reuses that one source of truth. The verify/apply/transform stages stay internal — you reach their effect through KoelClient/ChatSession, never by composing them directly.

Writing your own

An interceptor is a plain class implementing Interceptor. The Adapter Cookbook walks through a custom auth/error interceptor against the conformance contract; the recipes retry & auth and PII redaction show the built-ins in use.