Skip to main content
Middleware lets you hook into an agent run without touching the model, the tools, or the agent class. A middleware can observe what happens, transform the configuration and streamed chunks, or react to the run’s terminal outcome. You describe one as a class implementing IMiddleware and register it with decorator.middleware().

Defining middleware

The only required member is getName; every hook is optional. Implement just the hooks you need:
The type parameter on IMiddleware<TContext> types the runtime context read via ctx.context. Add it to an agent’s getMiddlewares, or pass it per request:

Lifecycle hooks

Hooks fire across the run’s lifecycle. All are optional. The three terminal hooks (onFinish, onAbort, onError) are mutually exclusive: exactly one fires per run.

Composition

Middleware composes in the order it appears in the array: the agent’s own middleware first, then any passed per request. How the hooks combine depends on their role. The piped hooks (onConfig, onStructuredOutputConfig, onChunk) each receive the previous middleware’s output, so transformations chain. onBeforeToolCall is first-win, stopping at the first middleware that returns a decision. Everything else runs sequentially, one after another.

Transforming the request

Config hooks return a partial config that is shallow-merged into the run. Use onConfig to adjust the whole run, or reach for onBeforeModel when you need a per-iteration tweak like raising the temperature on a retry:
Return void (or nothing) to pass the config through unchanged.

Filtering chunks

onChunk sees every streamed chunk and decides its fate. Return a chunk to replace it, an array to expand it, null to drop it, or void to pass it through:
Dropped chunks are not seen by later middleware.
Use ctx.defer(promise) inside a hook to run side effects (logging, audit writes) without blocking the run — they are awaited as part of the run’s completion.