---
title: Handlers
description: Defining execution handlers with blocks.
---

# Handlers

Handlers define what happens when a trigger fires. They contain execution blocks that run in sequence.

## Handler Structure

```yaml
handlers:
  trigger-name:
    Block Name:
      type: block-type
      # block-specific properties
    
    Another Block:
      type: another-type
      # ...
```

Each block has a human-readable name (shown in debug UI) and a type that determines its behavior.

## Block Types

### next-message

Generate a response from the LLM:

```yaml
handlers:
  user-message:
    Respond to user:
      type: next-message
      # Uses main conversation thread by default
      # Display defaults to 'stream'
```

With options:

```yaml
Generate summary:
  type: next-message
  thread: summary           # Use named thread
  display: stream           # Show streaming content
  independent: true         # Don't add to main chat
  output: SUMMARY           # Store output in variable
  description: Generating summary  # Shown in UI
```

### add-message

Add a message to the conversation:

```yaml
Add user message:
  type: add-message
  role: user                # user | assistant | system
  prompt: user-message      # Reference to prompt file
  input: [USER_MESSAGE]     # Variables to interpolate
  display: hidden           # Don't show in UI
```

For internal directives (LLM sees it, user doesn't):

```yaml
Add internal directive:
  type: add-message
  role: user
  prompt: ticket-directive
  input: [TICKET_DETAILS]
  visible: false            # LLM sees this, user doesn't
```

### tool-call

Call a tool deterministically:

```yaml
Create ticket:
  type: tool-call
  tool: create-support-ticket
  input:
    summary: SUMMARY        # Variable reference
    priority: medium        # Literal value
  output: TICKET           # Store result
```

### set-resource

Update a persistent resource:

```yaml
Save summary:
  type: set-resource
  resource: CONVERSATION_SUMMARY
  value: SUMMARY            # Variable to save
  display: name             # Show block name
```

### start-thread

Create a named conversation thread:

```yaml
Start summary thread:
  type: start-thread
  thread: summary                    # Thread name
  model: anthropic/claude-sonnet-4-5 # Optional: different model
  thinking: low                      # Extended reasoning level
  maxSteps: 1                        # Tool call limit
  system: escalation-summary         # System prompt
  input: [COMPANY_NAME]              # Variables for prompt
```

### serialize-thread

Convert conversation to text:

```yaml
Serialize conversation:
  type: serialize-thread
  thread: main              # Which thread (default: main)
  format: markdown          # markdown | json
  output: CONVERSATION_TEXT # Variable to store result
```

## Display Modes

Every block has a `display` property:

| Mode | Default For | Behavior |
|------|-------------|----------|
| `hidden` | add-message | Not shown to user |
| `name` | set-resource | Shows block name |
| `description` | tool-call | Shows description |
| `stream` | next-message | Streams content |

## Complete Example

```yaml
handlers:
  user-message:
    # Add the user's message to conversation
    Add user message:
      type: add-message
      role: user
      prompt: user-message
      input: [USER_MESSAGE]
      display: hidden
    
    # Generate response (LLM may call tools)
    Respond to user:
      type: next-message
      # display: stream (default)

  request-human:
    # Step 1: Serialize conversation for summary
    Serialize conversation:
      type: serialize-thread
      format: markdown
      output: CONVERSATION_TEXT
    
    # Step 2: Create separate thread for summarization
    Start summary thread:
      type: start-thread
      thread: summary
      model: anthropic/claude-sonnet-4-5
      thinking: low
      system: escalation-summary
      input: [COMPANY_NAME]
    
    # Step 3: Add request to summary thread
    Add summarize request:
      type: add-message
      thread: summary
      role: user
      prompt: summarize-request
      input:
        - CONVERSATION: CONVERSATION_TEXT
    
    # Step 4: Generate summary
    Generate summary:
      type: next-message
      thread: summary
      display: stream
      description: Summarizing your conversation
      independent: true
      output: SUMMARY
    
    # Step 5: Save to resource
    Save summary:
      type: set-resource
      resource: CONVERSATION_SUMMARY
      value: SUMMARY
    
    # Step 6: Create support ticket
    Create ticket:
      type: tool-call
      tool: create-support-ticket
      input:
        summary: SUMMARY
        priority: medium
      output: TICKET
    
    # Step 7: Add directive for response
    Add directive:
      type: add-message
      role: user
      prompt: ticket-directive
      input: [TICKET_DETAILS: TICKET]
      visible: false
    
    # Step 8: Respond to user
    Respond:
      type: next-message
```

## Block Input Mapping

Map variables to block inputs:

```yaml
# Simple list (variable name = prompt variable)
input: [USER_MESSAGE, COMPANY_NAME]

# Mapping (different names)
input:
  - CONVERSATION: CONVERSATION_TEXT  # CONVERSATION in prompt = CONVERSATION_TEXT
  - TICKET_DETAILS: TICKET
```

## Independent Blocks

Use `independent: true` for content that shouldn't go to the main chat:

```yaml
Generate summary:
  type: next-message
  thread: summary
  independent: true   # Output stored in variable, not main chat
  output: SUMMARY
```

This is useful for:
- Background processing
- Summarization in separate threads
- Generating content for tools

