Behaviour for middleware that wraps the agent loop.
Middleware runs at defined hook points:
:before_completion- Before calling the provider:after_completion- After provider response with :end_turn (final state):after_compaction- After context compaction occurs (only fires when messages changed):after_tool_request- After provider response with :tool_use (gates tool execution):after_tool_execution- After tools have been executed:on_error- When an error occurs
Middleware can modify state (e.g., add logging, enforce policies, track metrics) but should not change the fundamental loop behavior.
Summary
Callbacks
Called at the specified hook point. Returns modified state,
{:block, reason} for :before_tool_call to prevent execution,
or {:halt, reason} to stop the entire agent loop immediately.
Functions
Runs all middleware for a given hook point.
Runs :before_tool_call middleware for a single tool call.
Types
@type call_result() :: Alloy.Agent.State.t() | {:block, String.t()} | {:halt, String.t()} | {:edit, map()}
@type hook() ::
:before_completion
| :after_completion
| :after_compaction
| :after_tool_request
| :after_tool_execution
| :on_error
| :before_tool_call
| :session_start
| :session_end
Callbacks
@callback call(hook(), Alloy.Agent.State.t()) :: call_result()
Called at the specified hook point. Returns modified state,
{:block, reason} for :before_tool_call to prevent execution,
or {:halt, reason} to stop the entire agent loop immediately.
Functions
@spec run(hook(), Alloy.Agent.State.t()) :: Alloy.Agent.State.t() | {:halted, String.t()}
Runs all middleware for a given hook point.
Middleware can return {:halt, reason} to stop processing immediately
and mark the agent as halted. Returns either the final state or
{:halted, reason} tuple.
@spec run_before_tool_call(Alloy.Agent.State.t(), map()) :: :ok | {:block, String.t()} | {:edit, map()} | {:halted, String.t()}
Runs :before_tool_call middleware for a single tool call.
Injects the tool call into state.config.context[:current_tool_call]
before running middleware. Returns :ok, {:block, reason},
{:edit, modified_call}, or {:halted, reason}.
The {:edit, modified_call} return lets middleware rewrite tool arguments
before execution. The modified call must keep the same :id and :name.
Note: {:edit, ...}, {:block, ...}, and {:halt, ...} all stop the
middleware chain immediately. The first middleware that returns one of
these wins — subsequent middleware modules will not see the tool call.