Skip to main content
Agent middleware is the current extension API. It replaces the older BeforeToolCall, AfterToolCall, and model hook APIs. Embed agents.NoopMiddleware, override the operations you need, and register the value in AgentConfig.Middlewares. Middleware wraps the operation: call next to continue, return your own response to short-circuit it, or return an error to fail it.

Wrap a tool call

This complete middleware refuses a destructive tool when the application has not allowed it in the run context:
Set Middlewares: []agents.Middleware{policy.ToolPolicy{}} when constructing the agent. Derive permission values from authenticated application state, not model-generated arguments. The descriptor is plain data, including the schema, metadata, and annotations; the actual tool may run in another process. IsDeclaredDestructive only matches an explicit declaration. For a conservative policy, use IsDestructive, whose unset default is true. Annotations are hints, not proof that a tool is safe.

Refusal versus failure

  • Return agents.ToolCallResult(call, reason), nil to refuse one call while allowing the model to continue.
  • Return an error created by the middleware to end the run. agents.IsToolCallAborted(err) identifies these failures.
  • Return next’s error unchanged to preserve tool-error handling. A tool’s own error is reported to the model rather than reclassified as a middleware failure.
Use ToolCallResult when creating an answer so the call and result IDs remain paired. Do not mutate shared descriptors or returned response objects in place.

Available wrappers

Wrappers nest in registration order: the first middleware is outermost, and results return through the chain in reverse order. Shared middleware must be safe for concurrent calls. Under Temporal and Restate, middleware runs inside the activity or durable step that performs the operation. It can run again on retries. Make audit writes, charging, and other side effects idempotent.

Agent middleware versus provider middleware

AgentConfig.Middlewares accepts agents.Middleware (also hastekit.AgentMiddleware). hastekit.WithMiddleware configures the separate model-provider chain for retries, fallback, and provider operations. The two APIs run at different boundaries; see provider configuration.

Built-in middleware

pkg/agents/middleware includes retry/fallback policies, attachment handling, and tool response limits. See retries and timeouts, attachments, and tool output limits. Stop and timestamp middleware are installed at built-in execution boundaries. Completion timestamps are saved on response metadata and become stored message timestamps. Custom durable adapters must arrange for side effects and time reads to happen inside their recorded steps.