IMiddleware and register it with decorator.middleware().
Defining middleware
The only required member isgetName; every hook is optional. Implement just the hooks you need:
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.| Hook | When it fires |
|---|---|
setup | First, before any onConfig — provision capabilities for later middleware. |
onConfig | At init and once per agent iteration — observe or transform the run config. |
onBeforeModel | Immediately before each model call — adjust per-iteration knobs. |
onStructuredOutputConfig | Before the final structured-output call — transform its config and JSON Schema. |
onStart | Once, when the run starts. |
onIteration | At the start of each agent loop iteration. |
onChunk | For every streamed chunk — pass through, replace, expand, or drop it. |
onToolPhaseComplete | After all tool calls in an iteration are processed. |
onUsage | Once per iteration that reports token usage. |
onFinish | Terminal — the run completed normally. |
onAbort | Terminal — the run was aborted. |
onError | Terminal — an unhandled error occurred. |
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. The hooks combine differently depending on their role:- Piped —
onConfig,onStructuredOutputConfig, andonChunkeach receive the previous middleware’s output, so transformations chain. - First-win —
onBeforeToolCallstops at the first middleware that returns a decision. - Sequential — the remaining hooks simply run one after another.
Transforming the request
Config hooks return a partial config that is shallow-merged into the run. UseonConfig to adjust the whole run, or onBeforeModel for per-iteration tweaks like raising the temperature on a retry:
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:
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.