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: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), nilto 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.
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.