Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Event Handlers

Agents respond to events through handlers. Each handler runs when its corresponding event occurs.

on start

Runs when the agent is spawned:

agent Worker {
    on start {
        print("Worker started!");
        emit(42);
    }
}

Every agent must have an on start handler — it’s where the agent’s main logic lives.

on error

Handles errors propagated by try:

agent Researcher {
    topic: String

    on start {
        let result = try infer("Summarize: {self.topic}");
        emit(result);
    }

    on error(e) {
        print("Research failed: " ++ e);
        emit("unavailable");
    }
}

When a try expression fails, control jumps to on error. Without an on error handler, the agent will panic.

Message Handling

For agents that receive messages, use the receives clause with receive():

enum Command {
    Ping,
    Shutdown,
}

agent Worker receives Command {
    on start {
        loop {
            let msg: Command = receive();
            match msg {
                Ping => print("Pong!"),
                Shutdown => break,
            }
        }
        emit(0);
    }
}

See Messaging for details.

Handler Order

  1. on start runs first, exactly once
  2. on error runs if a try expression fails
  3. After emit, the agent terminates

emit

The emit expression signals that the agent has produced its result:

agent Calculator {
    a: Int
    b: Int

    on start {
        let result = self.a + self.b;
        emit(result);  // Agent is done
    }
}

After emit:

  • The agent’s result is available to whoever awaited it
  • The agent proceeds to cleanup (on stop)
  • No more messages are processed

Emit Type Consistency

All emit calls in an agent must have the same type:

agent Example {
    on start {
        if condition {
            emit(42);      // Int
        } else {
            emit("error"); // Error: expected Int, got String
        }
    }
}

Handler Scope

Each handler has its own scope. Variables don’t persist between handlers:

agent Example {
    on start {
        let x = 42;
        // x is only visible here
        emit(0);
    }

    on error(e) {
        // x is not visible here
        // Use agent fields for persistent state
        emit(1);
    }
}

Use agent fields (accessed via self) for state that needs to persist.