Skip to main content
Use this when a supervisor should stop answering directly and let a specialist own the next turn, or when a temporary gate must run before the final specialist takes over.

Concept

Switching the active agent is done through HANDOFF. 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_authenticated is ever set, is_authenticated != true evaluates to true (undefined is treated as not-equal-to-true), so the very first turn always routes to Authentication_Agent.
  • Authentication_Agent is a temporary child (EXPECT_RETURN: true): it gathers is_authenticated, completes once that field IS SET, and ON_RETURN maps its result back into the supervisor’s own is_authenticated variable and resumes intent routing.
  • On the next pass through HANDOFF, is_authenticated == true now holds, so the compound conditions on Account_Service_Agent/Billing_Agent can match and transfer ownership permanently (EXPECT_RETURN: false).
  • None of these conditions are rewritten by the compiler — equality comparisons like is_authenticated == true and intent.category == "account_service" handle an unset variable correctly on their own (they simply evaluate false), so the trace shows your literal authored condition text.
  • Since HISTORY is omitted from every HANDOFF, 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, DELEGATE is an alternative to HANDOFF + EXPECT_RETURN: true for 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 (MAP above) 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_authenticated set) and confirm it routes to Authentication_Agent first.
  • 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/MAP application 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).
  • MAP targets a variable the parent actually declares and routes on.
  • A fallback route exists for intents that don’t match account_service or billing after authentication — this example doesn’t include one.
  • Any variable assumed to already exist (like customer_id above) has a real declared source in your project.
  • Temporary vs. permanent switching is chosen deliberately per route, not by accident.

Common mistakes

Troubleshooting