Alloy.Middleware behaviour (alloy v0.10.1)

Copy Markdown View Source

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

call_result()

@type call_result() ::
  Alloy.Agent.State.t()
  | {:block, String.t()}
  | {:halt, String.t()}
  | {:edit, map()}

hook()

@type hook() ::
  :before_completion
  | :after_completion
  | :after_compaction
  | :after_tool_request
  | :after_tool_execution
  | :on_error
  | :before_tool_call
  | :session_start
  | :session_end

Callbacks

call(hook, t)

@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

run(hook, state)

@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.

run_before_tool_call(state, tool_call)

@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.