Overview
ON_START handler
TheON_START handler executes once when a new session initializes, before the agent processes any user input. Use it for greeting messages, initial tool calls, and variable initialization.
Syntax
ON_START properties
Template references in ON_START
You can reference named templates in the RESPOND value:welcome from the agent’s TEMPLATES: block.
ON_START with tool call
Guarding a CALL with WHEN
AnON_START or hook CALL can carry an indented WHEN: guard so it only runs when the condition holds:
Conditional greeting
Use aBRANCHES: block to vary the greeting based on session context. Each branch is an IF
(or CONDITION) with a fallback ELSE, and can carry RESPOND, MESSAGE_KEY, VOICE, rich
content, and ACTIONS.
ON_START language resolution
A reasoning agent with no FLOW can resolve the caller’s language from a startupSET/CALL and apply it before the welcome renders:
SET/CALL and before branch selection; a BRANCHES DO: list (below) can set the language too. On Gemini Live specifically, ON_START already runs before the initial provider setup, so the first connection uses this language in legacy, runtime_guarded, and runtime_owned orchestration modes — the channel must have the language configured in its own supported-language policy, or an unresolved/unsupported value is rejected without changing the policy default.
Start-time HANDOFF
ABRANCHES: entry (or a bare top-level HANDOFF:) can hand the session to another declared local agent at start, before the first customer message is even understood. A branch’s DO: list runs these actions in order, by default after the welcome renders:
If the list sets the interaction language, the prefix through the last language directive (before any
HANDOFF) runs before the welcome renders instead, and the rest after:
HANDOFF: <Agent> (bare, bulleted, or the sole entry of a top-level DO:) runs only when no branch matched — a matched branch with no DO: list never falls back to it. With no branches at all, a bare HANDOFF: is instead decided by that declared route’s own WHEN (and ENABLED_WHEN), checked once at session start:
How it runs, by start type:
- Eager start (no customer message yet — Web SDK/web-debug connect, pipeline voice answer, an identified agent-assist welcome): the welcome is delivered, then the child starts on its own with no synthetic customer message and speaks its own opener. Order: entry agent’s
before_agentoutput, the welcome, the handoff announcement (if the experience mode speaks one), the child’sbefore_agentoutput, the child’s own first output. ARETURN: truechild stays active after that unanswered opener; ordinary return rules apply from the customer’s first real message on. - Lazy start (the first customer message starts the session — HTTP chat, LiveKit, SDK
initialMessage): the welcome is not delivered, and that first message is forwarded to the child once — after the entry agent’s input guardrails/constraints (and, for a FLOW entry, itsbefore_turnhook) — taking precedence over the entry agent’s own first-turn routing. - Realtime speech-to-speech sessions run the list’s
SET/CALLbut never the handoff itself; the entry agent keeps the session.
dsl_on_start_handoff trace (executed, blocked, failed, call_failed, superseded_by_tool_hook, skipped_mode) with a reason and identifiers only — never customer data. When the handoff doesn’t run: a route WHEN simply not met shows nothing, not even ON_FAILURE text; a refused or failed handoff (a handoff guardrail, an unloaded target) shows only what the route’s ON_FAILURE authors (RESPOND shows its text; CONTINUE, or no ON_FAILURE, shows nothing — never a generic failure message) — except ESCALATE, which escalates the session instead of keeping it with the entry agent; a target an ENABLED_WHEN switched off follows the normal authored-route rule (use ON_FAILURE: CONTINUE to keep the start silent).
After the start, the route behaves as an ordinary handoff — RETURN, CONTEXT, and ON_RETURN apply as usual — and the child never runs its own ON_START (which fires once per session, for the entry agent only). A route WHEN that reads start data stays true for the rest of the session, so a later turn can still route back to that agent after it returns. If the handoff should really happen only at start, put the condition on a branch IF: and leave the route’s own WHEN unset, or clear the triggering value on return. Pairing with ENABLED_WHEN cleared on return (ENABLED_WHEN: promo_open == true with ON_RETURN: set: promo_open = false) also stops the model from choosing that agent on later turns.
Caller data asserted by the browser (public-key SDK init) is not verified. When a start-time routing decision matters, mint the caller data on your own server through the customer bootstrap endpoint rather than trusting a client-asserted value in a branch
IF: or route WHEN.HOOKS
TheHOOKS: block defines actions that run at four lifecycle points. Unlike ON_START, hooks fire repeatedly throughout the session.
Syntax
Hook points
Hook action properties
Each hook supports the same set of action properties:SET_INTERACTION_LANGUAGE and CLEAR_INTERACTION_LANGUAGE are action-list actions — like SET and CLEAR, they can appear in any action list (ON_START, HOOKS, ON_ACTION handlers). Use them to pin the language the agent responds in — for example, after detecting a caller’s preferred language or reading it from a profile.
The value is classified when the agent is compiled:
- Literal tag — a value that canonicalizes as BCP-47 (
es,fr-CA, …) is validated and fixed at compile time. - Dynamic value — anything else that looks like a session-variable path (
user.preferred_language), a{{template}}, or a${dotted.path}expression is resolved at runtime, using the same value resolution as an ordinarySET. - Anything that is neither a valid literal tag nor a recognized dynamic form is a compile error.
A dynamic value is resolved and re-validated at runtime: if it doesn’t resolve to a canonical BCP-47 tag, or resolves to a language the voice channel isn’t configured to support, the override is not applied and the session keeps its current language.
WHEN / RESPOND branches
A hook can respond conditionally: aWHEN: <condition> with an indented RESPOND: "<text>" is a respond branch. Branches are checked in declaration order, after the hook’s own CALL and SET run, and the first branch whose condition holds wins:
- In a reasoning agent’s
before_turnhook, a matched branch’s text becomes the turn’s reply and the model is not called for that turn. Input guardrails and escalation triggers still run first and can still intervene. - In other hooks (
before_agent,after_agent,after_turn), a matched branch’s text is sent as a message at that point in the lifecycle. - A
WHENthat can’t be evaluated does not hold — evaluation falls through to the next branch (or to an unconditionalRESPOND, if present). - A branch can sit next to an unconditional
RESPOND:in the same hook; the branches are the conditional alternative, evaluated independently.
Example: audit logging hook
Example: turn timing
ON_ERROR handlers
TheON_ERROR: block defines how the agent responds to specific error types. Each handler matches an error type and specifies a response, optional retry logic, and a follow-up action.
Syntax
Error types
The errortype (the handler’s key) is matched against a fixed, canonical set — a handler whose
key is not one of these is flagged at compile time (ON_ERROR_HANDLER_TYPE_DRIFT) because it can
never match at runtime:
Use
SUBTYPE (single value) or SUBTYPES (array) to match more finely within a type — both keys are case-insensitive but conventionally written uppercase.
Inline shorthand
For a handler that only takes a terminal action, use the single-line formerror_name: ACTION:
Error handler properties
Retry strategies
Then actions
Step-level error handlers
Error handlers can be overridden at the step level within a flow. Step-level handlers take precedence over agent-level handlers for the same error type.Step-level error handler properties
Error handler resolution order
- Step-level handlers are checked first, matching on error type and optional subtypes.
- Agent-level handlers (
ON_ERROR:block) are checked if no step-level handler matches. - If no handler matches, the runtime uses the default error message from the
MESSAGES:block (error_default).
MESSAGES
The top-levelMESSAGES: block overrides the default system messages the runtime uses for errors,
constraint violations, gathering, handoffs, escalation, completion, and voice/telephony prompts. Any
key you don’t set keeps its built-in default, and the templates support {{}} interpolation.
Common message keys
MESSAGES: accepts any message key, not only the ones above — including error-subtype
messages (error_tool_timeout, error_llm_error, error_validation, …), multi-intent UX prompts
(multi_intent_disambiguate_header, multi_intent_queued_notice, …), handoff/routing messages
(handoff_message, handoff_message_voice, routing_message), voice prompts (voice_repeat,
voice_nomatch, voice_noinput, …), and the greeting / out_of_scope messages. Set only the
keys you want to override.Filler overrides (voice)
On a voice channel, the runtime speaks a short filler phrase (for example “One moment.”) to avoid dead air while a tool call, reasoning step, handoff, delegation, extraction, or constraint check runs.MESSAGES: lets you override these with deterministic, author-controlled text using filler_<operation> keys:
Add a
_<language> suffix to supply a localized variant — either a bare BCP-47 primary subtag (filler_tool_call_fr) or a full region tag (filler_tool_call_fr-CA). At runtime the first match wins, checking the region tag before the bare subtag, and the operation-specific key before the general fallback:
filler_<operation>_<region-tag> → filler_<operation>_<language> → filler_<operation> → filler_general_<region-tag> → filler_general_<language> → filler_general → the platform’s built-in localized fallback.
A key with a region tag (
filler_tool_call_fr-CA) contains a hyphen — write it unquoted as a plain key: value line; both the .agent.abl and .agent.yaml formats parse the hyphen correctly.Related articles:
- Memory & Constraints — variables initialized in ON_START and hooks
- Multi-Agent & Supervisor — ESCALATE and HANDOFF used in THEN actions
- Rich content — voice and rich content in handler responses