Configure Agentforce Agent → Human Escalation
Wire an Agentforce agent so it can hand a conversation off to a human agent through an Omni-Channel queue. This skill configures and verifies the escalation surfaces, then reports a single machine-readable JSON verdict. It is idempotent per developer name and refuses to write to production orgs. It runs standalone or as a stage in a larger setup flow (after the agent is created and active).
The agent, queue, outbound flow name, routed context object, service channel, and failure thresholds are all inputs, so the same skill serves any scenario (IT employee support, customer messaging, etc.) — the caller supplies the scenario-specific identifiers.
This skill is the
authoritative owner of agent-to-human (outbound) escalation.
service-agentforce-channel-configure
may wire a basic outbound flow +
block in the same pass as inbound channel setup, but it delegates full escalation configuration and verification here.
The honest boundary — what is deterministic vs. directive vs. runtime
Escalation in Agentforce is assembled from surfaces with different verifiability:
- Deterministic (this skill writes and/or round-trip verifies via the Metadata/Data API): on the escalation topic (); on the agent's Messaging planner surface (), where the SAME block couples to
outboundRouteType=OmniChannelFlow
on a Messaging-class surface; a QueueBased outbound with a non-null ; a human queue with a for the context object, its bound to the queue, and at least one active human member (a queue with zero members can never route an escalation to a human, so it never reaches ); and the agent Active after republish.
- Directive / native policy (authored as Agent Script instructions, not a metadata counter): the default two consecutive failures threshold plus per-topic overrides (e.g. password reset = one failure). There is no metadata field that counts failed turns — the threshold is instruction text, verified by an ADK eval rubric, not a runtime read. See references/failure-policy.md.
- Runtime / manual only: the actual conversational trigger, creation, and same-session context preservation. These cannot be driven headlessly — they are a documented manual test + eval rubric. See references/runtime-verification.md.
Authoring models — classic vs next-gen (NGA)
Salesforce exposes two Agentforce authoring models, and Phase 1b auto-detects which the org supports from its metadata types. The routing infrastructure (queue,
, members,
, outbound
) is
identical in both; only the agent-side escalation surface differs.
- classic — org exposes + . The agent surface is (topic) + (planner Messaging surface). This surface is authored in Phase 4 (hand-edited, or via the optional pass) and round-trip verified in Phase 7.
- nga — org exposes (Agent Script) and no . There is no metadata; the agent surface is a reachable action (Service agents) or a create-record action (Employee agents, which cannot use ), and the queue routing is carried entirely by the deterministic routing half. This skill verifies that surface from the retrieved bundle and delegates its authoring to .
- none — neither surface is exposed → the skill fails closed before any write.
Detection can be pinned with
AUTHORING_MODEL_OVERRIDE=classic|nga
when both coexist. Full rules, the Service-vs-Employee split, and the NGA verdict set live in
references/nga-escalation.md.
Scope
- In scope: enabling the escalation topic (); wiring on the Messaging surface; deploying the bundled QueueBased outbound ; create-or-adopt of the human queue ( for the context object) and its ; authoring the failure-threshold directives; republish + reactivate; and a deterministic config round-trip verdict. Idempotent re-run.
- Out of scope: creating/activating the agent; inbound channel transport ( on the ) and the agent's outbound block (owned by
service-agentforce-channel-configure
); provisioning the messaging channel itself (service-digital-engagement-channel-configure
); broader Omni-Channel routing infrastructure; standalone queue provisioning outside an escalation context; and any production-org write.
Inputs
| Input | Positional | Env | Default |
|---|
| Org alias | 1 (required) | — | — |
| Agent developer name | 2 | | |
| Queue developer name | 3 | | |
| Escalation topic API name | 4 | ESCALATION_TOPIC_API_NAME
| (unset — surface unverified) |
| Planner bundle API name | 5 | | (unset — surface unverified) |
| Outbound flow developer name | — | | Human_Escalation_Outbound_Flow
|
| Outbound flow label | — | | flow name with → space |
| Context object (routed sObject) | — | | |
| Service channel dev name / label | — | / | / |
| Human members to add if empty | — | | (unset) |
| Failure threshold (consecutive failed attempts before hand-off) | — | DEFAULT_FAILURE_THRESHOLD
| |
| Threshold directive authored (out-of-band flag) | — | | |
For a non-
context object (e.g.
,
), pass the matching
/
.
Preconditions
- CLI installed and authenticated to the target org (
sf org display -o <alias>
shows Connected). All calls use .
- ≥ 18 and on PATH (the deterministic classifiers and JSON assembly).
- The agent exists and its latest is Active. This skill fails closed if not.
- For handoff, an active exists. This skill fails closed if none is found. Other context objects use a different transport and are not gated on .
- Sandbox / trial CDO / Developer Edition org. The skill refuses to configure escalation on a real production customer org.
If a precondition fails,
surfaces the raw error — do not fabricate state; surface it and stop.
Workflow
Phase 0 — Production write-guard (mandatory, first write-gate)
Before any write, the orchestrator queries
(
,
,
) and computes
. If the org is a real production customer org, the skill emits
and stops. This is enforced in
scripts/verify-and-configure.sh
— never bypass it.
Phase 1 — Preconditions (read-only, fail closed)
- Agent Active — SOQL (+ latest ), classified deterministically by scripts/classify-agent-active.mjs. Not found or inactive → block with the activation click-path.
- Transport channel present — for , SOQL
MessagingChannel WHERE IsActive=true
; zero rows → block with a pointer to service-digital-engagement-channel-configure
.
Phase 2 — Human queue (create-or-adopt) + members
Resolve the queue by DeveloperName. If missing, deploy a
(canonical Metadata API element order:
,
,
) with a
for
. For an adopted queue missing the binding, add the context object additively via the Data API. Create the
if absent and bind it to the queue (
Group.QueueRoutingConfigId
), re-querying to confirm the bind persisted.
Members (human routability): the queue must have at least one active human member — otherwise the escalation routes to an empty queue. The skill counts
rows that resolve to an active
. If there are none and you pass
(comma-separated usernames), it validates each against the username grammar up front, resolves the active
, and POSTs the
; it then re-counts. If the queue still has zero active human members the verdict is
with
queueHasActiveDirectUserMember
in
(an active direct user is necessary — not sufficient — for human routability; full Omni permission-set + presence-status eligibility is proven only at runtime). When there is no eligible member, the skill also skips QueueRoutingConfig binding and outbound-flow activation, so it never wires an empty queue. Use an approved Omni provisioning workflow when richer member reconciliation is required.
Phase 3 — Outbound QueueBased RoutingFlow
Deploy the bundled outbound flow template
assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml, substituting the flow name/label, queue DeveloperName, and service-channel tokens. The flow looks the queue up by DeveloperName at runtime (portable, no hardcoded Id). After deploy,
FlowDefinitionView.ActiveVersionId
must be non-null.
Phase 4 — Escalation topic + planner outbound route (doc-driven edits)
These two edits require per-org topic/bundle discovery, so they are authored explicitly (retrieve → edit → deploy), then verified by the orchestrator when you pass the topic/bundle API names. Follow references/escalation-mechanism.md:
- Retrieve the escalation topic (), set
<canEscalate>true</canEscalate>
, deploy.
- Retrieve the agent's , add (,
outboundRouteName=<FLOW_DEVELOPER_NAME>
, outboundRouteType=OmniChannelFlow
) on the Messaging planner surface, deploy. The verifier couples name + type inside the SAME block on a Messaging-class surface (via scripts/extract-outbound-route.mjs) — a bundle with the right name and an type in unrelated blocks will not pass.
Phase 5 — Failure-threshold directives (native policy)
The failure threshold and any per-topic overrides are
directives, not enforced counters — there is no metadata field that counts failed turns (see
references/failure-policy.md). The orchestrator does
not write these into the agent; it only renders
DEFAULT_FAILURE_THRESHOLD
(default
) into the local directive template
assets/escalation-thresholds.instructions.md — pass e.g.
DEFAULT_FAILURE_THRESHOLD=3
for a three-attempt scenario — and reports that value under
threshold.default_failures
(with
threshold.directive_rendered
) as verdict evidence. Authoring the rendered directive into the agent's Agent Script is a manual/out-of-band step; set
on the verifier run to record
. The directive is eval-verified, never a headless runtime read, so it never blocks the deterministic verdict.
Phase 6 — Republish + reactivate
After the Phase 4/5 edits, republish and reactivate the agent so they take effect. These are
manual operator commands for the hand-edited path; the orchestrator itself only publishes/deactivates/reactivates the agent inside the optional
pass (see "Running it"):
bash
sf agent validate authoring-bundle --api-name "$AGENT_DN" --target-org "$ORG" --json
sf agent publish authoring-bundle --api-name "$AGENT_DN" --target-org "$ORG" --json
sf agent activate --api-name "$AGENT_DN" --target-org "$ORG"
Phase 7 — Deterministic verification (single JSON verdict)
The orchestrator round-trips every deterministic surface, assembles an evidence object, and classifies it with scripts/verify-escalation-config.mjs into one of three states so a missing escalation policy can never masquerade as fully configured:
- — all deterministic surfaces satisfied and the failure-threshold directive is authored.
ROUTING_CONFIGURED_POLICY_PENDING
— routing and the agent escalation surface are verified, but the failure-threshold directive () is not yet confirmed. Author it ( / ) and re-run to reach .
- — one or more deterministic surfaces are missing (see ).
Note the NGA escalation-surface detection (
scripts/classify-nga-escalation.mjs) is an
advisory preflight — it matches for a reachable
/ create-record action in the authoring bundle but does not prove runtime reachability from the topic. Treat a passing NGA surface check as a strong signal, not end-to-end runtime proof; confirm the live handoff with the Phase 8 runtime step.
Phase 8 — Runtime / manual proof (out of headless scope)
Confirm the conversational trigger,
creation, and same-session context preservation with a live conversation — see
references/runtime-verification.md.
Running it
bash
bash scripts/verify-and-configure.sh <org-alias> \
[<agent-developer-name>] [<queue-developer-name>] \
[<escalation-topic-api-name>] [<planner-bundle-api-name>]
- Positionals 2–5 also read from env (see the Inputs table). Further scenario inputs are env-only: , , , , , .
- previews (read-only) and stops before any write.
- marks the directive policy as authored for the verdict.
- (optional, run mode) — deterministically author the two doc-driven surfaces instead of hand-editing XML, following the Agentforce lifecycle in
agentforce-generate/references/known-issues.md
(Issue 18): publish (regenerate the compiled bundle) → retrieve the topic + in metadata format (--target-metadata-dir … --unzip
) → deactivate the agent (deploys fail while it is active) → apply a schema-aware, XSD-ordered, loss-less, idempotent patch (scripts/patch-escalation-surfaces.mjs
) that sets (after , before ) and inserts (outboundRouteType=OmniChannelFlow
, before on a Messaging-class planner surface, targeting the outbound flow) → deploy with → activate (fail-closed; the agent is never left inactive silently) → re-read the latest so the verdict reflects the freshly-authored version. It requires the topic and/or bundle API names, runs only after the membership gate (never wires an empty queue), and refuses (blocks) rather than guess if it cannot find a safe, XSD-ordered patch point. Omit it to keep the skill verify-only.
- The and surfaces are verified only when you pass
<escalation-topic-api-name>
/ <planner-bundle-api-name>
(they are authored via the doc-driven edits in Phase 4).
Typical two-pass flow: run once to stand up the queue + outbound flow and preconditions; perform the Phase 4/5/6 doc-driven edits; then re-run with the topic + bundle API names and
to reach
.
Output contract
A single JSON object to stdout:
json
{
"skill": "service-agentforce-human-escalation-configure",
"status": "CONFIGURED",
"agent": { "developer_name": "Support_Agent", "active": true },
"queue": { "developer_name": "Human_Support_Queue", "id": "00G...", "active_direct_user_member_count": 1 },
"outbound_flow": { "api_name": "Human_Escalation_Outbound_Flow", "active_version_id": "301..." },
"escalation_topic": "Escalate_To_Human",
"planner_bundle": "Support_Agent",
"config_verification": {
"verdict": "CONFIGURED",
"deterministicPass": true,
"directivePass": true,
"checks": [],
"missing": []
},
"next_steps": [],
"manual_actions": [],
"blocking_issue": null
}
- : (all deterministic surfaces satisfied), (some
config_verification.missing
), or (bad input / auth / production / precondition; exit 1).
- Re-running an already-configured org is a no-op that returns (idempotent).
Idempotency
Existing queue,
,
, queue members, and an already-active outbound flow are reused, not recreated (members are only added when the queue has zero active members and
is supplied; already-present members are left untouched). The doc-driven edits (
,
) are set-if-absent. A second run against a fully configured org performs no writes and returns
.
Verification
Run the bundled escalation contract tests after changing this skill or its scripts:
bash
python3 scripts/tests/test_escalation_contracts.py