> ## Documentation Index
> Fetch the complete documentation index at: https://docs.standardagentbuilder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# defineHook

> Define lifecycle hooks for agent execution

## Overview

`defineHook` creates a hook that runs at specific points during agent execution. Each hook has a unique `id` and must be explicitly referenced by a prompt or agent to execute.

```typescript theme={null}
import { defineHook } from '@standardagents/spec';

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

## Type Definition

```typescript theme={null}
function defineHook<K extends HookName>(
  options: HookDefinitionOptions<K>
): HookDefinitionResult<K>;

interface HookDefinitionOptions<K extends HookName> {
  hook: K;
  id: string;
  execute: HookSignatures[K];
}

interface HookDefinitionResult<K extends HookName> {
  hook: K;
  id: string;
  execute: HookSignatures[K];
}

type HookName =
  | 'after_thread_created'
  | 'after_subagent_created'
  | 'after_system_message'
  | 'filter_messages'
  | 'prefilter_llm_history'
  | 'before_create_message'
  | 'before_update_message'
  | 'after_create_message'
  | 'after_update_message'
  | 'before_store_tool_result'
  | 'after_tool_call_success'
  | 'after_tool_call_failure';
```

## Parameters

<ParamField body="hook" type="HookName" required>
  The hook type. Determines when the hook runs and what parameters `execute` receives.
</ParamField>

<ParamField body="id" type="string" required>
  Unique identifier for this hook. Must be snake\_case (lowercase letters, numbers, underscores, starting with a letter). Referenced by prompts and agents in their `hooks` array.
</ParamField>

<ParamField body="execute" type="Function" required>
  Async function that executes when the hook is triggered. Parameters are automatically typed based on the `hook` type.
</ParamField>

## Hook Scoping

Hooks must be referenced by a prompt or agent to execute:

```typescript theme={null}
// Prompt references hooks by ID
definePrompt({
  name: 'customer_support',
  hooks: ['log_message_creation', 'limit_to_20_messages'],
  // ...
});

// Agent can define default hooks (used when prompt has none)
defineAgent({
  name: 'support_agent',
  hooks: ['inject_context'],
  // ...
});
```

**Resolution priority:**

1. If the current prompt declares `hooks`, only those run
2. If the prompt has no `hooks` but the agent does, agent hooks run
3. If neither declares hooks, no hooks execute

## Hook Types

### Transformation Hooks

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

#### filter\_messages

Runs before messages are transformed into chat completion format.

```typescript theme={null}
defineHook({
  hook: 'filter_messages',
  id: 'keep_completed',
  execute: async (state, rows) => {
    return rows.filter(row => row.status === 'completed');
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="rows" type="Message[]">
  Messages from storage
</ParamField>

**Returns:** `Message[]` - Filtered/modified messages

#### prefilter\_llm\_history

Runs before messages are sent to the LLM.

```typescript theme={null}
defineHook({
  hook: 'prefilter_llm_history',
  id: 'add_dynamic_context',
  execute: async (state, messages) => {
    return [
      { role: 'system', content: `Current time: ${new Date().toISOString()}` },
      ...messages,
    ];
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="messages" type="LLMMessage[]">
  Messages about to be sent to LLM
</ParamField>

**Returns:** `LLMMessage[]` - Modified messages

#### before\_create\_message

Runs before a message is inserted into the database.

```typescript theme={null}
defineHook({
  hook: 'before_create_message',
  id: 'tag_assistant_messages',
  execute: async (state, message) => {
    if (message.role === 'assistant') {
      message.name = state.agentConfig.title;
    }
    return message;
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="message" type="Record<string, unknown>">
  Message about to be created
</ParamField>

**Returns:** `Record<string, unknown>` - Modified message

#### before\_update\_message

Runs before a message is updated in the database.

```typescript theme={null}
defineHook({
  hook: 'before_update_message',
  id: 'add_updated_at',
  execute: async (state, messageId, updates) => {
    return {
      ...updates,
      updated_at: Date.now(),
    };
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="messageId" type="string">
  ID of message being updated
</ParamField>

<ParamField body="updates" type="Record<string, unknown>">
  Updates being applied
</ParamField>

**Returns:** `Record<string, unknown>` - Modified updates

#### before\_store\_tool\_result

Runs before a tool result is stored in the database.

```typescript theme={null}
defineHook({
  hook: 'before_store_tool_result',
  id: 'sanitize_results',
  execute: async (state, toolCall, toolResult) => {
    return { ...toolResult, sanitized: true };
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="toolCall" type="Record<string, unknown>">
  The tool call that was executed
</ParamField>

<ParamField body="toolResult" type="Record<string, unknown>">
  The result to be stored
</ParamField>

**Returns:** `Record<string, unknown>` - Modified tool result

#### after\_tool\_call\_success

Runs after a tool executes successfully. Can modify the result or convert to a different message type.

```typescript theme={null}
import { defineHook } from '@standardagents/spec';
import { injectMessage } from '@standardagents/builder';

defineHook({
  hook: 'after_tool_call_success',
  id: 'convert_user_input',
  execute: async (state, call, result) => {
    if (call.function.name === 'get_user_input') {
      await injectMessage(state, {
        role: 'user',
        content: result.result || '',
      });
      return null; // Remove tool call from history
    }
    return result;
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="call" type="ToolCall">
  The tool call that was executed
</ParamField>

<ParamField body="result" type="ToolResult">
  Result from the tool
</ParamField>

**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.

```typescript theme={null}
defineHook({
  hook: 'after_tool_call_failure',
  id: 'friendly_errors',
  execute: async (state, call, error) => {
    return {
      status: 'error',
      error: `The ${call.function.name} operation is temporarily unavailable.`,
    };
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="call" type="ToolCall">
  The tool call that failed
</ParamField>

<ParamField body="error" type="ToolResult">
  The error result
</ParamField>

**Returns:** `ToolResult | null` - Modified error result or null to remove

### 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.

```typescript theme={null}
defineHook({
  hook: 'after_create_message',
  id: 'log_messages',
  execute: async (state, message) => {
    await fetch('https://analytics.example.com/events', {
      method: 'POST',
      body: JSON.stringify({
        event: 'message_created',
        thread_id: state.threadId,
        role: message.role,
      }),
    });
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="message" type="Record<string, unknown>">
  The created message
</ParamField>

**Returns:** `void`

#### after\_update\_message

Runs after a message is updated. Use to track status changes.

```typescript theme={null}
defineHook({
  hook: 'after_update_message',
  id: 'track_updates',
  execute: async (state, message) => {
    console.log(`Message ${message.id} updated`);
  },
});
```

<ParamField body="state" type="ThreadState">
  Current execution context
</ParamField>

<ParamField body="message" type="Message">
  The updated message
</ParamField>

**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

```typescript theme={null}
// This hook throws, but execution continues
defineHook({
  hook: 'filter_messages',
  id: 'buggy_filter',
  execute: 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/
    ├── limit_messages.ts
    ├── log_analytics.ts
    └── sanitize_pii.ts
```

**Requirements:**

* File names can be anything (hook is identified by its `id`)
* Default export required
* Multiple hooks of the same type are supported (each with a unique `id`)

## Quick Reference

| Hook                       | Type      | When It Runs                             |
| -------------------------- | --------- | ---------------------------------------- |
| `after_thread_created`     | Event     | After thread creation, before execution  |
| `after_subagent_created`   | Event     | On parent after subagent thread creation |
| `after_system_message`     | Transform | After system message render              |
| `filter_messages`          | Transform | Before message transformation            |
| `prefilter_llm_history`    | Transform | Before sending to LLM                    |
| `before_create_message`    | Transform | Before INSERT                            |
| `before_update_message`    | Transform | Before UPDATE                            |
| `before_store_tool_result` | Transform | Before storing tool result               |
| `after_create_message`     | Event     | After INSERT                             |
| `after_update_message`     | Event     | After UPDATE                             |
| `after_tool_call_success`  | Transform | After successful tool                    |
| `after_tool_call_failure`  | Transform | After failed tool                        |
