Agent Hooks
Hooks intercept specific points in the agent execution pipeline. Use them to observe or modify behavior during agent operations: before a request starts, when preparing messages for the LLM, before and after tool execution, or after completion.
Defining Hooks
Define hooks using the createHooks helper function. Pass the resulting object to the Agent constructor, or to individual method calls like generateText and streamText.
import {
Agent,
createHooks,
messageHelpers,
type AgentTool,
type AgentOperationOutput,
type VoltAgentError,
type OnStartHookArgs,
type OnEndHookArgs,
type OnPrepareMessagesHookArgs,
type OnPrepareModelMessagesHookArgs,
type OnToolStartHookArgs,
type OnToolEndHookArgs,
type OnHandoffHookArgs,
} from "@voltagent/core";
// Define a collection of hooks using the helper
const myAgentHooks = createHooks({
/**
* Called before the agent starts processing a request.
*/
onStart: async (args: OnStartHookArgs) => {
const { agent, context } = args;
console.log(`[Hook] Agent ${agent.name} starting interaction at ${new Date().toISOString()}`);
console.log(`[Hook] Operation ID: ${context.operationId}`);
},
/**
* Called after VoltAgent sanitizes UI messages but before the LLM receives them.
* `rawMessages` contains the unsanitized list for inspection or metadata recovery.
*/
onPrepareMessages: async (args: OnPrepareMessagesHookArgs) => {
const { messages, rawMessages, context } = args;
console.log(`Preparing ${messages.length} sanitized messages for LLM`);
// Add timestamp to each message
const timestamp = new Date().toLocaleTimeString();
const enhanced = messages.map((msg) => messageHelpers.addTimestampToMessage(msg, timestamp));
if (rawMessages) {
// Access raw message structure for audit logging
console.debug(`First raw message parts:`, rawMessages[0]?.parts);
}
return { messages: enhanced };
},
/**
* Called after UI messages are converted into provider-specific ModelMessage objects.
*/
onPrepareModelMessages: async (args: OnPrepareModelMessagesHookArgs) => {
const { modelMessages, uiMessages } = args;
console.log(`Model payload contains ${modelMessages.length} messages`);
// Inject a system message if none exists
if (!modelMessages.some((msg) => msg.role === "system")) {
return {
modelMessages: [
{
role: "system",
content: [{ type: "text", text: "Operate within safety budget" }],
},
...modelMessages,
],
};
}
return {};
},
/**
* Called after the agent completes a request (success or failure).
*/
onEnd: async (args: OnEndHookArgs) => {
const { agent, output, error, context } = args;
if (error) {
console.error(`[Hook] Agent ${agent.name} finished with error:`, error.message);
console.error(`[Hook] Error Details:`, JSON.stringify(error, null, 2));
} else if (output) {
console.log(`[Hook] Agent ${agent.name} finished successfully.`);
// Log usage or inspect output type
if ("usage" in output && output.usage) {
console.log(`[Hook] Token Usage: ${output.usage.totalTokens}`);
}
if ("text" in output && output.text) {
console.log(`[Hook] Final text length: ${output.text.length}`);
}
if ("object" in output && output.object) {
console.log(`[Hook] Final object keys: ${Object.keys(output.object).join(", ")}`);
}
}
},
/**
* Called before a tool executes.
*/
onToolStart: async (args: OnToolStartHookArgs) => {
const { agent, tool, context, args: toolArgs } = args;
console.log(`[Hook] Agent ${agent.name} starting tool: ${tool.name}`);
console.log(`[Hook] Tool arguments:`, toolArgs);
},
/**
* Called after a tool completes or throws an error.
*/
onToolEnd: async (args: OnToolEndHookArgs) => {
const { agent, tool, output, error, context } = args;
if (error) {
console.error(`[Hook] Tool ${tool.name} failed:`, error.message);
console.error(`[Hook] Tool Error Details:`, JSON.stringify(error, null, 2));
} else {
console.log(`[Hook] Tool ${tool.name} completed with result:`, output);
}
},
/**
* Called when a task is handed off from a source agent to this agent.
*/
onHandoff: async (args: OnHandoffHookArgs) => {
const { agent, sourceAgent } = args;
console.log(`[Hook] Task handed off from ${sourceAgent.name} to ${agent.name}`);
},
});
// Pass hooks to the Agent constructor
const agentWithHooks = new Agent({
name: "My Agent with Hooks",
instructions: "An assistant demonstrating hooks",
model: "openai/gpt-4o",
hooks: myAgentHooks,
});
// Or define hooks inline
const agentWithInlineHooks = new Agent({
name: "Inline Hooks Agent",
instructions: "Another assistant",
model: "openai/gpt-4o",
hooks: {
onStart: async ({ agent, context }) => {
/* ... */
},
onEnd: async ({ agent, output, error, context }) => {
/* ... */
},
},
});
Passing Hooks to Methods
Pass hooks to generateText, streamText, generateObject, or streamObject to run hooks for that specific invocation only.
Method-level hooks do not override agent-level hooks. Both will execute. For most hooks, the method-level hook runs first, then the agent-level hook. For onPrepareMessages and onPrepareModelMessages, the method-level hook replaces the agent-level hook entirely.
const agent = new Agent({
name: "My Agent with Hooks",
instructions: "An assistant demonstrating hooks",
model: "openai/gpt-4o",
hooks: myAgentHooks,
});
await agent.generateText("Hello, how are you?", {
hooks: {
onEnd: async ({ context }) => {
console.log("End of generation for this invocation!");
},
},
});
For example, store conversation history only for specific endpoints:
const agent = new Agent({
name: "Translation Agent",
instructions: "A translation agent that translates text from English to French",
model: "openai/gpt-4o",
});
// for the translate endpoint, we don't want to store the conversation history
app.post("/api/translate", async (req, res) => {
const result = await agent.generateText(req.body.text);
return result;
});
// for the chat endpoint, we want to store the conversation history
app.post("/api/translate/chat", async (req, res) => {
const result = await agent.streamText(req.body.text, {
hooks: {
onEnd: async ({ context }) => {
await chatStore.save({
conversationId: context.conversationId,
messages: context.steps,
});
},
},
});
return result.textStream;
});
Available Hooks
All hooks receive a single argument object containing relevant information.