Concept
DELEGATE calls another agent as a sub-agent. The caller pauses, the sub-agent runs to completion, and control returns to the caller with a result. The customer stays with the caller the whole time.
HANDOFF is different. A handoff moves the conversation to another agent. Use DELEGATE for a lookup, a check, or a calculation. Use a handoff when the other agent must talk to the customer. See Use delegate instead of handoff and Use handoff instead of delegate for the decision.
A delegate is a one-shot internal call. The sub-agent cannot wait for the customer to answer a question. If it asks for input, the call fails.
The pieces of a delegate fit together in this order:
- Declare. The caller lists the sub-agent in a
DELEGATE:section. - Decide.
WHENandENABLED_WHENdecide whether the call is allowed. - Send.
INPUT(also writtenPASS) copies caller values to the sub-agent. - Run. The sub-agent runs once, within a timeout.
- Map back.
RETURNScopies sub-agent values into caller variables. The full result is also stored in one variable. - Fail safely.
ON_FAILUREdecides what happens on a failure or timeout.
A
DELEGATE: entry is the contract for all three. It sets INPUT, RETURNS, the timeout, and the failure policy.
Minimal working example
The caller asks for an order number, calls a refund checker, and tells the customer the result. Both agents are complete. Thecheck_refund_policy tool is a project-local assumption: bind it to your refund policy service in Project Tools.
order_idis gathered by the caller inask_order.INPUTcopies it to the sub-agent, which declares it inGATHER.eligibleandreasonare session values that the sub-agent sets in its flow.refund_eligibleandrefund_reasonare caller session values.RETURNSwrites them. Thetell_customerstep uses them.refund_checkreceives the full result. See The result envelope.
Refund_Checker is not in the compilation.
How it works
What the sub-agent receives
The platform starts a new thread for the sub-agent and passes three things:- The values from
INPUT. Each entry copies a caller variable to a sub-agent field. The source is a plain variable name or a dotted path such ascustomer.id. - The instruction text. A model-selected delegate writes it. A flow delegate uses the current customer message.
- Session metadata that the caller already holds.
INPUT does not evaluate expressions. A value such as quantity * 2 is not computed. Compilation warns with CEL_IN_INPUT_MAPPING. Compute the value with SET in an earlier step, then map the variable. A source that has no value is dropped, and the sub-agent field stays empty.
DELEGATE has no HISTORY setting and no MAP key. The parser ignores an unknown key without a message. If the sub-agent needs context, send it through INPUT.
If the sub-agent declares a required GATHER field, you must map it in INPUT. Otherwise validation fails with DELEGATE_CHILD_GATHER_INPUT_UNMAPPED, because the sub-agent cannot ask the customer.
The result envelope
When the sub-agent finishes, the platform builds one result object. It holds these fields:USE_RESULT names the caller variable that stores this object. Write a plain variable name, such as USE_RESULT: refund_check. If you omit it, the object is stored in delegate_result. A value that is not a variable name, such as "Present the fee breakdown", parses without a message and stores nothing.
How RETURNS maps values
RETURNS has two forms.
Map form. Write child_field: parent_variable. The platform reads child_field from the result and assigns it to parent_variable. It looks in this order: a result field of that name, a key in the JSON response, the sub-agent session values, and the sub-agent state context. In the example, eligible is read from the sub-agent session values.
Schema form. Write field: type, such as eligible: boolean. Every entry must be a type: string, number, integer, boolean, object, array, json, unknown, a capitalized type name, or an array of one of these. The platform copies the values into the result returns field and does not write caller variables.
Rules that apply to both forms:
- A key that is missing in the sub-agent result is skipped.
- If
RETURNSis declared and no key maps, the delegate fails with “completed without required return values”. The failure path runs. - An empty string never overwrites a caller variable that already holds a value. Return
nullto clear one on purpose. - Compilation warns with
UNKNOWN_DELEGATE_RETURN_FIELDwhen the sub-agent does not declare or produce the child field. Fix the name, or declare the field in the sub-agent. - Each target variable must not be a reserved system name.
The timeout
The timeout is chosen in this order:TIMEOUTon theDELEGATE:entry.- The runtime delegate timeout, which the platform operator can set.
- The general runtime timeout.
- 30 seconds.
TIMEOUT as bare milliseconds or a number ending in ms, s, or m. Any other text, such as soon, fails validation with INVALID_TIMEOUT_SYNTAX. On timeout, the platform cancels the sub-agent, waits a short grace period for it to stop, and runs the failure policy.
Conditions
WHEN decides whether this call is valid. A value that is an expression, such as order_id IS SET, is evaluated against caller data. A value written as natural language only guides the model. It passes only when the model chose the delegate.
Use IS SET to guard a call on a present value. A test such as order_id != "" does not skip the call when the variable is unset. A // outside quotes starts a comment inside WHEN. Text after it is not part of the condition.
ENABLED_WHEN decides whether the target is available at all. It must be a deterministic expression over session data, such as caller.tier == "gold". Natural language and conditions that read the current customer turn fail with ENABLED_WHEN_SEMANTIC_REFERENCE. An empty ENABLED_WHEN fails with ENABLED_WHEN_EMPTY. When the target is disabled, the model does not see the delegate tool, and a flow or ON_START call is refused.
Safety checks
The platform refuses a delegate to the calling agent, a delegate that would create a loop (A calls B calls A), a chain deeper than 10, and a target that does not exist. Each one runs the failure policy. A role matters. An agent withBEHAVIOR role router cannot declare DELEGATE. Validation fails with AGENT_RETURNED_WORK_REQUIRES_ORCHESTRATOR. Use role orchestrator, or no role.
The sub-agent cannot end with a terminal handoff (RETURN: false). Validation fails with DELEGATE_CHILD_HANDOFF_UNSUPPORTED.
Failure policy
ON_FAILURE accepts three actions:
Any other action, such as
RETRY 2, fails validation with INVALID_DELEGATE_FAILURE_ACTION. A misspelled key such as ON_FAIL: is ignored without a message, so the default CONTINUE applies. Check the spelling.
What the failure does next depends on the starter:
- Model tool call. The model gets the failure as the tool result and can answer or try something else. A timeout, an escalation, or a
RESPONDtext ends the model loop for that turn. - Flow step action. A failed delegate ends the step and sends a failure response.
ON_FAILUREstill applies:RESPONDtext becomes the response, andESCALATEescalates. ON_START. A failed or refused delegate ends the startup turn. A target switched off byENABLED_WHENwith an authoredON_FAILURE: CONTINUEcarries on instead.
Common variations
Model-selected delegate in a reasoning agent
Do not write a flow. The agent model sees adelegate_to_Refund_Checker tool, built from PURPOSE, WHEN, and the INPUT keys. The model calls it when WHEN allows. Use this when the model should decide whether the call is needed. This example reuses Refund_Checker from above.
ON_FAILURE does not run. A supervisor declares DELEGATE: the same way. See Design a supervisor routing agent.
Delegate at session start
WriteDELEGATE: inside ON_START: to run a sub-agent once before the first turn. The target needs a DELEGATE: entry. The entry WHEN is evaluated at start. If it is not met, the platform skips the delegate silently and the start sequence continues. INPUT sources must exist at start, so set them in ON_START first. A DELEGATE written inside an ON_START DO: list is not a supported startup action.
order_id in session metadata. When ON_START combines DELEGATE with a start-time handoff, compilation warns with ON_START_DELEGATE_WITH_HANDOFF. Keep one of them. See Use an ON_START welcome.
Map the reply text with ON_RETURN
A flow stepDO: action can add ON_RETURN with a MAP. The source keys read the result envelope. This is useful for the reply text. Set RETURN: true so the flow continues after the call. Without RETURN: true, the step ends after the delegate.
RETURN and ON_RETURN are the only keys allowed under a DELEGATE action. Any other key fails validation. A flow step action can name a target that has no DELEGATE: entry. It then has no INPUT, no RETURNS, and the default CONTINUE failure policy. Declare the entry.
Typed result without caller variables
Use the schema form ofRETURNS when you want a typed result in the envelope and no new caller variables.
Limit availability with ENABLED_WHEN
ON_FAILURE runs. With no DELEGATE: entry, the customer gets a standard “option unavailable” line. See Route with supervisor priority for related availability rules.
Share memory with the sub-agent
MEMORY_GRANTS lists session or persistent memory paths that the sub-agent can read. Each path must be a declared memory path or in the execution_tree namespace. An undeclared path fails validation with INVALID_HANDOFF_MEMORY_GRANT. Grant the minimum. Prefer INPUT for plain values.
Remote sub-agents
LOCATION: REMOTE, ENDPOINT, and PROTOCOL parse and compile on a DELEGATE: entry. Validation checks the endpoint URL and the timeout. At run time, the delegate path resolves only agents in your project. A remote target fails with “Agent not found”. Use a HANDOFF entry with LOCATION: REMOTE for an external agent. See Design reusable agents and modules.
Verification
- Validate the caller and every sub-agent together. Confirm no
DELEGATE_CHILD_GATHER_INPUT_UNMAPPEDorUNKNOWN_DELEGATE_RETURN_FIELDmessages remain. - Test success. Send an order number that your tool accepts. Confirm the caller replies with the eligibility and reason, and that the caller session holds
refund_eligibleandrefund_reason. - Test a failed call. Make the tool fail or time out. Confirm the customer sees your
ON_FAILUREtext, or that the escalation starts. - Test a missing value. Start without
order_id. Confirm theWHENguard blocks the call. - Inspect the trace. Expect
delegate_start, thenagent_lifecycle(before and after),thread_return, anddelegate_complete. On success,delegate_completeshowssuccess: trueand the result. On failure it showssuccess: falseand the error text. - For
RETURNS, find thedelegate_return_mappingdecision events. Each shows the field,setoroverwrite, and a redacted value. Ablockedoutcome means the platform refused the value. - Look for a
delegate_return_contract_unsatisfieddecision. It means noRETURNSkey mapped. - For a timeout, expect
delegate_completewith the error “Delegate timeout” and adelegate_cancellation_gracedecision. - For routing context, see Debug routing traces.
Production readiness checklist
- Every sub-agent exists in the same project and has its tools bound and permissioned.
- Every required sub-agent
GATHERfield has anINPUTmapping. - Every
INPUTsource has a value before the call. - Every
RETURNSchild field exists in the sub-agent, and every parent variable is used by the caller. TIMEOUTfits the slowest tool in the sub-agent. Do not rely on the 30 second default for slow services.ON_FAILUREis set on purpose. UseRESPONDfor customer-facing paths andESCALATEwhere a human must act.- No sub-agent asks the customer questions. Use a returning handoff for those.
- Tests cover success, missing input, tool failure, timeout, and a disabled target.
- Monitoring watches
delegate_completeevents withsuccess: false.