Skip to main content

Hooks

What it is

Hooks are callbacks that fire at specific points in the agent event loop. They let you observe, modify, or interrupt agent behavior without changing the agent's core logic.

Problem it solves

Without hooks, you'd need to subclass Agent or wrap every tool call to add logging, auditing, PII redaction, or human-in-the-loop approval. Hooks give you these interception points without modifying the agent or tool code.

How to use it

Register hooks on the agent's Hooks property:

var agent = new Agent(model, systemPrompt: "...", toolProviders: [new MyTools()]);

// Log every tool call
agent.Hooks.Register<BeforeToolCallEvent>(async (e, ct) =>
{
Console.WriteLine($"Calling tool: {e.ToolName} with args: {e.Input}");
});

// Log every tool result
agent.Hooks.Register<AfterToolCallEvent>(async (e, ct) =>
{
Console.WriteLine($"Tool {e.ToolName} returned: {e.Result.Content}");
});

The four hook events

EventWhen it firesWhat you can do
BeforeModelCallEventBefore each model API callInspect/modify the messages being sent
AfterModelCallEventAfter each model API callInspect the raw model response
BeforeToolCallEventBefore each tool executionInspect args, interrupt execution
AfterToolCallEventAfter each tool executionInspect result, modify it

Human-in-the-loop

Set e.Interrupt = true in a BeforeToolCallEvent hook to pause execution before a sensitive tool call:

agent.Hooks.Register<BeforeToolCallEvent>(async (e, ct) =>
{
if (e.ToolName == "DeleteFile")
{
Console.WriteLine($"About to delete: {e.Input}");
Console.Write("Approve? (y/n): ");
var response = Console.ReadLine();
if (response?.ToLower() != "y")
e.Interrupt = true; // cancels the tool call
}
});

When Interrupt = true, the tool is not called. The agent receives a cancellation result and typically stops or asks the user what to do next.

Audit logging

agent.Hooks.Register<BeforeToolCallEvent>(async (e, ct) =>
{
await auditLog.WriteAsync(new AuditEntry
{
Timestamp = DateTimeOffset.UtcNow,
ToolName = e.ToolName,
Input = e.Input.GetRawText(),
UserId = currentUser.Id
});
});

PII redaction

agent.Hooks.Register<AfterModelCallEvent>(async (e, ct) =>
{
// Scan model output for PII before it reaches tool calls
if (ContainsPii(e.Response))
e.Response = RedactPii(e.Response);
});

Trade-offs

Hooks run synchronously in the event loop. A slow hook (e.g., one that makes a network call) adds latency to every tool call. Keep hooks fast or use fire-and-forget patterns for non-blocking operations like audit logging.