Event Handlers
Agents respond to events through handlers. Each handler runs when its corresponding event occurs.
on start
Runs when the agent is summoned:
agent Worker {
on start {
print("Worker started!");
yield(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 divine("Summarize: {self.topic}");
yield(result);
}
on error(e) {
print("Research failed: " ++ e);
yield("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,
}
}
yield(0);
}
}
See Messaging for details.
Handler Order
on startruns first, exactly onceon errorruns if atryexpression fails- After
yield, the agent terminates
yield
The yield expression signals that the agent has produced its result:
agent Calculator {
a: Int
b: Int
on start {
let result = self.a + self.b;
yield(result); // Agent is done
}
}
After yield:
- The agent’s result is available to whoever awaited it
- The agent proceeds to cleanup (
on stop) - No more messages are processed
Yield Type Consistency
All yield calls in an agent must have the same type:
agent Example {
on start {
if condition {
yield(42); // Int
} else {
yield("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
yield(0);
}
on error(e) {
// x is not visible here
// Use agent fields for persistent state
yield(1);
}
}
Use agent fields (accessed via self) for state that needs to persist.