Skip to main content

Overview

defineHook creates a hook that runs at specific points during agent execution.
import { defineHook } from '@standardagents/builder';

export default defineHook('after_create_message', async (state, message) => {
  console.log('Message created:', message.id);
});

Type Definition

function defineHook<T extends HookName>(
  name: T,
  handler: HookHandler<T>
): Hook<T>;

type HookName =
  | 'filter_messages'
  | 'prefilter_llm_history'
  | 'before_create_message'
  | 'before_update_message'
  | 'after_create_message'
  | 'after_update_message'
  | 'after_tool_call_success'
  | 'after_tool_call_failure';

Parameters

name
HookName
required
The hook type. Determines when the hook runs and what parameters it receives.
handler
HookHandler
required
Async function that executes when the hook is triggered. Parameters vary by hook type.

Hook Types

Transformation Hooks

These hooks receive data, can modify it, and return the modified version.

filter_messages

Runs before SQL rows are transformed into messages. Use to filter or modify raw message data.
defineHook('filter_messages', async (state, rows) => {
  // rows: MessageRow[]
  return rows.filter(row => row.status === 'completed');
});
state
FlowState
Current execution context
rows
MessageRow[]
Raw message rows from the database
Returns: MessageRow[] - Filtered/modified rows

prefilter_llm_history

Runs before messages are sent to the LLM. Use to modify context or add dynamic data.
defineHook('prefilter_llm_history', async (state, messages) => {
  // Add dynamic system message
  return [
    { role: 'system', content: `Current time: ${new Date().toISOString()}` },
    ...messages,
  ];
});
state
FlowState
Current execution context
messages
Message[]
Messages about to be sent to LLM
Returns: Message[] - Modified messages

before_create_message

Runs before a message is inserted into the database. Use to add metadata or modify content.
defineHook('before_create_message', async (state, message) => {
  if (message.role === 'assistant') {
    message.name = state.agentConfig.title;
  }
  return message;
});
state
FlowState
Current execution context
message
Message
Message about to be created
Returns: Message - Modified message

before_update_message

Runs before a message is updated in the database.
defineHook('before_update_message', async (state, messageId, updates) => {
  return {
    ...updates,
    updated_at: Date.now(),
  };
});
state
FlowState
Current execution context
messageId
string
ID of message being updated
updates
Partial<Message>
Updates being applied
Returns: Partial<Message> - Modified updates

after_tool_call_success

Runs after a tool executes successfully. Can modify the result or convert to a different message type.
import { defineHook, injectMessage } from '@standardagents/builder';

defineHook('after_tool_call_success', async (state, call, result) => {
  if (call.function.name === 'get_user_input') {
    // Convert tool result to user message
    await injectMessage(state, {
      role: 'user',
      content: result.result || '',
    });
    return null; // Remove tool call from history
  }
  return result;
});
state
FlowState
Current execution context
call
ToolCall
The tool call that was executed
result
ToolResult
Result from the tool
Returns: ToolResult | null - Modified result or null to remove

after_tool_call_failure

Runs after a tool fails. Can modify the error or suppress the failure.
defineHook('after_tool_call_failure', async (state, call, error) => {
  console.error(`Tool ${call.function.name} failed:`, error);

  // Return a modified error message
  return {
    status: 'error',
    error: `The ${call.function.name} operation is temporarily unavailable.`,
  };
});
state
FlowState
Current execution context
call
ToolCall
The tool call that failed
error
Error
The error that occurred
Returns: ToolResult - Modified error result

Event Hooks

These hooks run after an event and don’t return anything.

after_create_message

Runs after a message is inserted. Use for logging, analytics, or webhooks.
defineHook('after_create_message', async (state, message) => {
  await fetch('https://analytics.example.com/events', {
    method: 'POST',
    body: JSON.stringify({
      event: 'message_created',
      thread_id: state.threadId,
      message_id: message.id,
      role: message.role,
    }),
  });
});
state
FlowState
Current execution context
message
Message
The created message
Returns: void

after_update_message

Runs after a message is updated. Use to track status changes.
defineHook('after_update_message', async (state, messageId, updates) => {
  if (updates.status === 'completed') {
    console.log(`Message ${messageId} completed`);
  }
});
state
FlowState
Current execution context
messageId
string
ID of updated message
updates
Partial<Message>
The updates that were applied
Returns: void

Error Handling

All hooks are wrapped in error handling:
  1. If a hook throws, the error is logged
  2. Execution continues with original data
  3. The framework doesn’t crash
// This hook throws, but execution continues
defineHook('filter_messages', async (state, rows) => {
  throw new Error('Something went wrong!');
});

// Framework behavior:
// 1. Logs: [Hooks] ✗ Error running filter_messages hook: Something went wrong!
// 2. Returns original rows (unmodified)
// 3. Continues execution normally

File Location

Hooks are auto-discovered from agents/hooks/:
agents/
└── hooks/
    ├── filter_messages.ts
    ├── after_create_message.ts
    └── after_tool_call_success.ts
Requirements:
  • File name must match hook name
  • One hook per file
  • Default export required

Quick Reference

HookTypeWhen It Runs
filter_messagesTransformBefore SQL → Message conversion
prefilter_llm_historyTransformBefore sending to LLM
before_create_messageTransformBefore INSERT
before_update_messageTransformBefore UPDATE
after_create_messageEventAfter INSERT
after_update_messageEventAfter UPDATE
after_tool_call_successTransformAfter successful tool
after_tool_call_failureTransformAfter failed tool