Concept
Switching the active agent is done throughHANDOFF. EXPECT_RETURN: false transfers ownership permanently to the child specialist — it becomes the active agent for the rest of the conversation. EXPECT_RETURN: true creates a temporary child thread that returns to the parent (via ON_RETURN), often for authentication or qualification — the supervisor stays the active agent in the long run, just pausing to delegate one sub-task.
The authentication-gate pattern below relies on one subtle but important behavior: on the very first turn, is_authenticated doesn’t exist yet. The condition is_authenticated != true still correctly evaluates to true in that case — the runtime treats an undefined variable as not equal to true — so the gate fires and routes to Authentication_Agent before the variable is ever set. You don’t need to special-case “unset” separately from “false.”
Minimal working example
customer_id, account_id, and conversation_summary are shown as passed context with no declared source in this example — treat them as project-local assumptions and declare them via MEMORY in your actual project. This example also has no fallback route if neither account_service nor billing matches after authentication — see the fallback-routing HowTo before shipping this pattern as-is.
How it works
- Before
is_authenticatedis ever set,is_authenticated != trueevaluates totrue(undefined is treated as not-equal-to-true), so the very first turn always routes toAuthentication_Agent. Authentication_Agentis a temporary child (EXPECT_RETURN: true): it gathersis_authenticated, completes once that fieldIS SET, andON_RETURNmaps its result back into the supervisor’s ownis_authenticatedvariable and resumes intent routing.- On the next pass through
HANDOFF,is_authenticated == truenow holds, so the compound conditions onAccount_Service_Agent/Billing_Agentcan match and transfer ownership permanently (EXPECT_RETURN: false). - None of these conditions are rewritten by the compiler — equality comparisons like
is_authenticated == trueandintent.category == "account_service"handle an unset variable correctly on their own (they simply evaluatefalse), so the trace shows your literal authored condition text. - Since
HISTORYis omitted from everyHANDOFF, each child (temporary or permanent) receives the full conversation history by default (the current platform default). - If you’d rather run the authentication step as a sub-agent call than a full route/return cycle,
DELEGATEis an alternative toHANDOFF+EXPECT_RETURN: truefor this kind of temporary gate — see the delegate-vs-handoff decision HowTo for the tradeoffs.
Common variations
- Temporary authentication child returns to supervisor (shown above).
- Final billing or account specialist owns the conversation permanently after the gate passes.
- A return handler maps child output into parent routing state (
MAPabove) so the next routing pass can use it.
Verification
- Parse and compile the ABL and confirm there are no parser or compiler errors/warnings.
- Test an utterance on a fresh session (no
is_authenticatedset) and confirm it routes toAuthentication_Agentfirst. - After authentication completes, test an account-service and a billing utterance and confirm permanent ownership transfer in each case.
- Inspect the trace for the selected target, the evaluated condition (matches your literal authored text), and the
ON_RETURN/MAPapplication after the authentication child returns.
Production readiness checklist
- The auth gate correctly handles the unset case on turn one (no special-casing needed, but verify it in a real trace).
MAPtargets a variable the parent actually declares and routes on.- A fallback route exists for intents that don’t match
account_serviceorbillingafter authentication — this example doesn’t include one. - Any variable assumed to already exist (like
customer_idabove) has a real declared source in your project. - Temporary vs. permanent switching is chosen deliberately per route, not by accident.