What is a handler?
A handler is a function that takes one side’s turns in a conversation. When a handler backs a side of an agent, the runtime calls the handler once for each inbound message. The runtime sends no model request for that side. A handler has one role: it backs a side of an agent. Three other definitions cover the remaining roles:- A hook changes or observes a turn that a prompt or a handler owns.
- A tool is a capability that a prompt chooses to call.
- An effect runs outside the conversation flow.
Defining a handler
Create a file inagents/handlers/ and export a defineHandler call as the default export. The file name is the handler name: agents/handlers/relay_pipe.ts defines the handler relay_pipe.
message is the complete persisted message, with its id, role, content, silent flag, metadata, and timestamps.
Referencing a handler
Reference a handler from thehandler property of a side in defineAgent. This is the only place that can reference a handler.
handler, label, and subagents. It does not accept maxSteps, stopOnResponse, stopTool, or the session bindings, because a handler turn has no steps.
Standard Agents Builder rejects a prompt whose tools or hooks array names a handler. The error is raised before the prompt’s side runs.
Execution model
A turn of a handler-backed side has three stages:- An inbound message arrives on the side’s perspective. Side A acts on
usermessages. Side B acts onassistantmessages. - The runtime calls
executeonce with the thread’sThreadStateand that message. executereturns or throws. The turn is complete.
maxSessionTurns, a handler turn counts toward that limit in the same way as a prompt turn.
Each handler call writes one row to the thread’s logs. The row names the handler and records its duration.
Delivery
Standard Agents Builder delivers each inbound message to the handler one time:- The handler runs once for each inbound message, in the order the messages were stored.
- One thread never runs two handler calls at the same time. A message that arrives during a handler call waits and is delivered after that call returns.
- A message whose handler call failed is not delivered again. Send the message again to retry.
- The runtime records the delivery position before each call. A restart during a call does not deliver that message a second time.
Errors
A handler that throws fails that delivery only:- The log row for the call records the error with
error_typehandler_execution_errorand a runtime origin. - The inbound message stays stored. Nothing is rolled back.
- The session continues. The next inbound message starts a new handler call.
state.stopSession().
What a handler can do
execute receives the same ThreadState that tools receive:
- Forward:
(await state.getChildThread(reference))?.queueMessage(...)sends a message to a child.state.notifyParent(...)sends one to a parent. - Respond:
state.injectMessage({ role, content })writes a message in the side’s own role. The role isassistantfor side A anduserfor side B. - Use children: the side’s
subagentsdeclarations make child instances available throughstate.children. A handler needs no lifecycle tools. - Control:
state.stopSession(),state.setStatus(),state.scheduleEffect(), the environment, the thread values, and the file system.
Avoiding message loops
A handler runs only when a message arrives. A handler that replies to every inbound message can still exchange messages with a prompt-backed side without end. Use one of these controls:- Reply only when a condition holds.
- Set
maxSessionTurnson the agent. - Call
state.stopSession()when the work is done.
Next steps
Agents
Choose what backs each side of an agent
Subagents
Declare the child relationships a handler forwards to
ThreadState
See the state object a handler receives
Effects
Schedule work that runs outside a turn