@salesforce/afv-skills 1.65.1 → 1.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-offline-experimentation-run/SKILL.md +16 -15
  3. package/skills/agentforce-offline-experimentation-run/scripts/sfap_replay.py +3 -245
  4. package/skills/dx-devops-work-item-manage/SKILL.md +34 -17
  5. package/skills/dx-devops-work-item-manage/examples/common-workflows.md +49 -0
  6. package/skills/dx-devops-work-item-manage/references/commit-api.md +116 -0
  7. package/skills/experience-lwc-widget-assess/SKILL.md +79 -0
  8. package/skills/experience-lwc-widget-migrate/SKILL.md +50 -0
  9. package/skills/platform-role-hierarchy-apply/SKILL.md +177 -0
  10. package/skills/platform-widget-generate/SKILL.md +58 -41
  11. package/skills/platform-widget-generate/references/widget-bundle-validation.md +14 -0
  12. package/skills/service-agentforce-contact-center-coordinate/SKILL.md +1 -1
  13. package/skills/service-agentforce-contact-center-coordinate/references/number_management_api.md +11 -1
  14. package/skills/service-agentforce-contact-center-coordinate/scripts/fetch-numbers.sh +12 -6
  15. package/skills/service-agentforce-contact-center-coordinate/scripts/lib.sh +16 -0
  16. package/skills/service-agentforce-contact-center-coordinate/scripts/procure-number.sh +2 -1
  17. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +24 -11
  18. package/skills/service-digital-engagement-channel-configure/SKILL.md +25 -14
  19. package/skills/service-digital-engagement-channel-configure/references/onboarding_contract.md +143 -0
  20. package/skills/service-digital-engagement-deployment-configure/SKILL.md +61 -12
  21. package/skills/service-digital-engagement-deployment-configure/references/connect_api_creation.md +14 -5
  22. package/skills/service-digital-engagement-deployment-configure/references/onboarding_contract.md +146 -0
  23. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +23 -13
  24. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +7 -6
  25. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +9 -10
  26. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +6 -6
  27. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +1 -1
  28. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +34 -25
  29. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +1 -1
  30. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +90 -86
  31. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/workflow.md +63 -38
@@ -55,7 +55,7 @@ All steps are sequential. If a step fails, stop and report the error rather than
55
55
 
56
56
  2. **Gather configuration** — use `AskUserQuestion` to collect **Country** (`US`/`CA`) and **Phone Number Type** (`10DLC`/`Toll Free`). Do not mark any option as recommended or default — present the choices neutrally. Wait for both before continuing.
57
57
 
58
- 3. **Fetch available numbers** — read `references/number_management_api.md` for the endpoint contract, then run `scripts/fetch-numbers.sh <alias> <country> <phoneType>`. Present the returned numbers as a numbered list and ask the user to select one — do not mark any number as recommended or default.
58
+ 3. **Fetch available numbers** — read `references/number_management_api.md` for the endpoint contract. Before fetching, use `AskUserQuestion` to offer an **optional** way to narrow results: ask whether the user wants numbers for a specific **area code** (when the type is `10DLC`) or **prefix** (when the type is `Toll Free`), or to list all available numbers. Present this neutrally with no recommended/default option; leaving it blank (or choosing to list all) preserves the unfiltered fetch. Then run `scripts/fetch-numbers.sh <alias> <country> <phoneType> [searchParam]`, passing the area code / prefix as the optional fourth argument only when the user supplied one. Present the returned numbers as a numbered list and ask the user to select one — do not mark any number as recommended or default. If a narrowed fetch returns no numbers, tell the user and offer to retry with a different area code / prefix or to list all available numbers.
59
59
 
60
60
  4. **Procure the selected number** — run `scripts/procure-number.sh <alias> <country> <phoneType> <number>`. On a non-2xx response, map the status via `references/number_management_api.md` and stop with a clear message.
61
61
 
@@ -21,11 +21,18 @@ Query parameters:
21
21
  |-------|--------|-------|
22
22
  | `countryCode` | `US`, `CA` | |
23
23
  | `phoneNumberType` | `10DLC`, `Toll Free` | URL-encode the space in `Toll Free` as `%20` |
24
+ | `searchParam` | area code (`10DLC`) or prefix (`Toll Free`) | **Optional.** Narrows results to numbers matching the area code / prefix. Omit to list all available numbers for the type. Passed through as-is (the API returns `400 BAD_REQUEST` on an invalid value). |
24
25
 
25
26
  ```bash
27
+ # All available Toll Free numbers for US
26
28
  sf api request rest \
27
29
  "/services/data/v68.0/connect/number-management/v1/numbers?countryCode=US&phoneNumberType=Toll%20Free" \
28
30
  --target-org <alias>
31
+
32
+ # Optional: narrow 10DLC numbers to a specific area code via searchParam
33
+ sf api request rest \
34
+ "/services/data/v68.0/connect/number-management/v1/numbers?countryCode=US&phoneNumberType=10DLC&searchParam=415" \
35
+ --target-org <alias>
29
36
  ```
30
37
 
31
38
  Response:
@@ -42,10 +49,13 @@ Response:
42
49
  sf api request rest \
43
50
  "/services/data/v68.0/connect/number-management/v1/number" \
44
51
  --method POST \
45
- --body '{"phoneNumberType":"Toll Free","country":"US","phoneNumber":"+14155551234"}' \
52
+ --body '{"phoneNumberType":"Toll Free","country":"US","phoneNumber":"+14155551234","contactCenterType":"Agentforce Contact Center"}' \
46
53
  --target-org <alias>
47
54
  ```
48
55
 
56
+ `contactCenterType` is a fixed internal value (`Agentforce Contact Center`) — always sent,
57
+ never gathered from the user.
58
+
49
59
  Success (`201`):
50
60
 
51
61
  ```json
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env bash
2
2
  # Fetch available default phone numbers for a country + phone-number type.
3
- # Usage: fetch-numbers.sh <org-alias> <country> <phone-type>
4
- # country: US | CA
5
- # phone-type: 10DLC | "Toll Free"
3
+ # Usage: fetch-numbers.sh <org-alias> <country> <phone-type> [search-param]
4
+ # country: US | CA
5
+ # phone-type: 10DLC | "Toll Free"
6
+ # search-param: optional — area code (10DLC) or prefix (Toll Free) to narrow
7
+ # results. Appended as the API's `searchParam` query param. Omit
8
+ # (or pass empty) to list all available numbers for the type.
6
9
  # Output: JSON { "phoneNumbers": [...] } on stdout.
7
10
 
8
11
  set -euo pipefail
@@ -10,12 +13,15 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
13
  source "$SCRIPT_DIR/lib.sh"
11
14
 
12
15
  if [[ "${1:-}" == "--help" || $# -lt 3 ]]; then
13
- echo "Usage: fetch-numbers.sh <org-alias> <country> <phone-type>" >&2
16
+ echo "Usage: fetch-numbers.sh <org-alias> <country> <phone-type> [search-param]" >&2
14
17
  [[ "${1:-}" == "--help" ]] && exit 0 || exit 2
15
18
  fi
16
19
 
17
- ALIAS="$1"; COUNTRY="$2"; PHONE_TYPE="$3"
20
+ ALIAS="$1"; COUNTRY="$2"; PHONE_TYPE="$3"; SEARCH_PARAM="${4:-}"
21
+
22
+ QUERY="countryCode=${COUNTRY}&phoneNumberType=$(urlenc_spaces "$PHONE_TYPE")"
23
+ [[ -n "$SEARCH_PARAM" ]] && QUERY="${QUERY}&searchParam=$(urlenc "$SEARCH_PARAM")"
18
24
 
19
25
  sf api request rest \
20
- "/${NM_BASE}/numbers?countryCode=${COUNTRY}&phoneNumberType=$(urlenc_spaces "$PHONE_TYPE")" \
26
+ "/${NM_BASE}/numbers?${QUERY}" \
21
27
  --target-org "$ALIAS"
@@ -13,6 +13,22 @@ NM_BASE="services/data/${API_VERSION}/connect/number-management/v1"
13
13
  # URL-encode a value's spaces (sufficient for country/phoneType query params).
14
14
  urlenc_spaces() { printf '%s' "${1// /%20}"; }
15
15
 
16
+ # Percent-encode a full value for safe use as a URL query-parameter value: only
17
+ # the RFC 3986 unreserved set stays literal, everything else becomes %XX. Needed
18
+ # for free-text values (e.g. a user-supplied area code / prefix) so reserved
19
+ # characters like & or = can't inject extra query parameters into the request.
20
+ urlenc() {
21
+ local s="$1" i c out=""
22
+ for (( i=0; i<${#s}; i++ )); do
23
+ c="${s:i:1}"
24
+ case "$c" in
25
+ [a-zA-Z0-9.~_-]) out+="$c" ;;
26
+ *) printf -v c '%%%02X' "'$c"; out+="$c" ;;
27
+ esac
28
+ done
29
+ printf '%s' "$out"
30
+ }
31
+
16
32
  # XML-escape a value for safe interpolation into flow-template stringValue/label nodes.
17
33
  # Free-text org data (queue names, agent labels) can contain &, <, >, ", ' — unescaped
18
34
  # they produce malformed XML and an opaque `sf project deploy start` parse error.
@@ -14,8 +14,9 @@ fi
14
14
 
15
15
  ALIAS="$1"; COUNTRY="$2"; PHONE_TYPE="$3"; NUMBER="$4"
16
16
 
17
+ # contactCenterType is a fixed internal value, not a user-supplied input.
17
18
  BODY=$(jq -n --arg t "$PHONE_TYPE" --arg c "$COUNTRY" --arg p "$NUMBER" \
18
- '{phoneNumberType:$t, country:$c, phoneNumber:$p}')
19
+ '{phoneNumberType:$t, country:$c, phoneNumber:$p, contactCenterType:"Agentforce Contact Center"}')
19
20
 
20
21
  sf api request rest \
21
22
  "/${NM_BASE}/number" \
@@ -6,9 +6,9 @@ Help Portal deploys an **Agentforce Concierge experience** on an LWR Experience
6
6
 
7
7
  The agent script does **not** change here — this is channel/site metadata around the agent.
8
8
 
9
- ## Execution model — headless with one operator pause
9
+ ## Execution model — Headless first, UI fallback
10
10
 
11
- Every step in §A–§S below is executed headlessly via `sf` CLI + Data API + Metadata API + Tooling API. There is exactly **one** step that requires the operator to click a Setup toggle: enabling the **Agentforce Orchestrator** OrgPerm (see §F.4). When the flow reaches that step, pause, present the operator with a direct Setup URL and clear instructions, and wait for them to reply `done` before continuing. Every other Setup-URL fallback in this document is a *last resort* — attempt the headless path first, and only fall back to UI if the headless path returns an unrecoverable error.
11
+ Every step in §A–§S below is executed headlessly via `sf` CLI + Data API + Metadata API + Tooling API. The Agentforce Orchestrator OrgPerm (§F.4) is enabled via a Connect API POST — no operator UI pause is required. Every Setup-URL fallback in this document is a *last resort* — attempt the headless path first, and only fall back to UI if the headless path returns an unrecoverable error.
12
12
 
13
13
  If a non-critical step fails (branding polish, suggestion themes, progressive-rendering toggle), log the failure, continue, and surface the gap to the operator at the end. Do NOT abort the deploy for cosmetic gaps.
14
14
 
@@ -50,7 +50,7 @@ At the end of the run, print the live customer-facing portal URL for the operato
50
50
  > | 4 | Channel + routing + escalation | §E — `sf agent activate` first; if `MESSAGING_CHANNEL_DEV_NAME` not passed in, delegate to `service-digital-engagement-channel-configure` to create the channel (deploy XML with `<sessionHandlerType>AgentforceServiceAgent</sessionHandlerType>` + `<sessionHandlerQueue>` only — `sessionHandlerAsa` is not accepted by the v67 Metadata API); then delegate to `service-agentforce-channel-configure` (Phase 1: queue, Phase 2: Data API PATCH of `SessionHandlerId` + `FallbackQueueId` on the MessagingChannel, Phase 3: escalation question) | SOQL confirms `SessionHandlerId=$BOT_ID`, `FallbackQueueId` set; `BotVersion.Status=Active`; escalation answered by operator |
51
51
  > | 6 | Retrieve + edit + deploy bundle | §F.0 — retrieve `DigitalExperienceBundle:site/$BUNDLE_NAME`; **edit `sfdc_cms__brandingSet/*/content.json` with the four colors from §A (§D)**; replace home placeholder with 4 Concierge components (empty `attributes: {}`); add `Conversation__c` route + `conversation` view (§F.0.a); flip `authenticationType`; deploy | Retrieve confirms components + route; branding JSON contains user-specified hex values |
52
52
  > | 7 | Wire Concierge runtime binding | §F.3.a–d — create `EmbeddedServiceConfig` via Connect API `POST /connect/embeddedmessaging/deployment/setup` (same as Web Chat §C.4, omit `hostDomain`; auto-generates `ESW_*` site + test page), insert NSSE, **deploy AND assign** guest permset to Site's `GuestUserId` | `/webruntime/api/…/concierge/config` returns `deploymentName + siteUrl + scrtUrl` **AND** `SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE AssigneeId=<GuestUserId>` returns the `<Bundle>_Guest_Concierge` row |
53
- > | 8 | **⏸ Enable Agentforce Orchestrator (operator pause)** | §F.4 — preflight SOQL on `AgenticCtxtDecorDefinition`; if not supported, pause and ask operator to flip the toggle at the printed Setup URL; wait for `done` reply; then `POST /connect/self-service/setup/agentOrchestrator {name, developerName, agentId}` | Connect GET returns orchestrator with agentId |
53
+ > | 8 | Enable Agentforce Orchestrator + create orchestrator binding | §F.4 — `POST /connect/self-service/setup/agentforceOrchestratorEnablement?includeAgentforceOrchestratorPerms=false` (headless, no operator pause); verify SOQL probe; then `POST /connect/self-service/setup/agentOrchestrator {name, developerName, agentId}` | Connect GET returns orchestrator with agentId |
54
54
  > | 9 | Deploy the bundle | §G — `sf project deploy start -m DigitalExperienceBundle:site/$BUNDLE_NAME` | Status: Succeeded |
55
55
  > | 10 | Publish the site | §I — `POST /connect/communities/$NETWORK_ID/publish`; PATCH `Network.Status=Live` | Site loads at `PUBLISHED_PORTAL_URL` |
56
56
  > | 11 | Network guest flags | §L.0 — PATCH `Network.OptionsGuestChatterEnabled=true AND OptionsGuestMemberVisibility=true` (single Data API call). ALSO verify auth ESC `AreGuestUsersAllowed=true` — flip via mdapi + republish if false. | `/concierge/config?asGuest=true` returns 200; SOQL confirms both Network flags + ESC flag true |
@@ -712,13 +712,13 @@ curl -sS "$PUBLISHED_PORTAL_URL/webruntime/api/services/data/v67.0/connect/self-
712
712
 
713
713
  All three fields must be non-null. If `deploymentName` is null, the bound ESC references a different site or MessagingChannel — verify the `<site>` in the ESC metadata is the LWR site (F.3.a).
714
714
 
715
- ### F.4 — Wire the Agentforce Orchestrator *(one operator pause, then fully headless)*
715
+ ### F.4 — Wire the Agentforce Orchestrator *(Headless first, UI fallback)*
716
716
 
717
- The Agentforce Orchestrator provides the personalized greeting, Action/Object recommendations, and Concierge proactive routing. The "Enable Agentforce Orchestrator" OrgPerm gates the agent-binding surfaces (Connect POST, Data API sObject, SOQL). Detect the toggle state with a SOQL write-probe, prompt one manual click if off, then continue fully headless.
717
+ The Agentforce Orchestrator provides the personalized greeting, Action/Object recommendations, and Concierge proactive routing. The "Enable Agentforce Orchestrator" OrgPerm gates the agent-binding surfaces (Connect POST, Data API sObject, SOQL). Enable it headlessly via `POST /connect/self-service/setup/agentforceOrchestratorEnablement`, verify with the SOQL probe, then create the orchestrator binding.
718
718
 
719
719
  **F.4.0 — Detection preflight.**
720
720
 
721
- Run the probe up to **2 times** before concluding the toggle is off. The `sf` CLI occasionally mixes warning output into stdout on the first call, which causes a false "not supported" parse. Re-running the identical query without waiting resolves the issue — no org state changes between attempts.
721
+ Run the SOQL probe up to **2 times** before concluding the toggle is off. The `sf` CLI occasionally mixes warning output into stdout on the first call causing a false "not supported" parse. Re-running the identical query without waiting resolves the issue — no org state changes between attempts.
722
722
 
723
723
  ```bash
724
724
  TOGGLE_STATE="UNKNOWN"
@@ -735,11 +735,24 @@ done
735
735
  ```
736
736
 
737
737
  - ✅ `TOGGLE_STATE=ON` → proceed with §F.4.2.
738
- - ❌ `TOGGLE_STATE=OFF` (both attempts) → toggle is genuinely OFF, **pause and prompt the operator**.
738
+ - ❌ `TOGGLE_STATE=OFF` (both attempts) → toggle is genuinely OFF → proceed to §F.4.1 (headless enable).
739
739
 
740
- **F.4.1 — ⏸ OPERATOR PAUSE: enable the Orchestrator OrgPerm.**
740
+ **F.4.1 — Headless enable via Connect API (fully headless — no operator pause).**
741
741
 
742
- If the SOQL probe returned "not supported", send this message verbatim to the operator, then wait for their reply:
742
+ Enable Agentforce Orchestrator headlessly:
743
+
744
+ > **`min_api_version: 68.0`** — this endpoint is only available on API v68.0+. On v67 orgs the POST returns `NOT_FOUND`; fall through to the UI path below.
745
+
746
+ ```bash
747
+ sf api request rest -o "$ORG" --method POST \
748
+ "/services/data/v68.0/connect/self-service/setup/agentforceOrchestratorEnablement?includeAgentforceOrchestratorPerms=false" \
749
+ --body '{}'
750
+ # → {"isSuccess": true}
751
+ ```
752
+
753
+ After the POST, re-run the §F.4.0 SOQL probe to confirm `TOGGLE_STATE=ON`. If the probe still returns "not supported" after a successful POST (can happen on a slow pod), wait 10 seconds and re-probe once more. If the probe is still off after that re-probe, fall back to the UI step below — do not continue to §F.4.2.
754
+
755
+ If the org **genuinely does not have this surface** (the POST itself returns `NOT_FOUND` or `UNSUPPORTED_OPERATION`), fall through to the legacy UI path as a last resort:
743
756
 
744
757
  > **One manual step needed.** Please open this URL and enable Agentforce Orchestrator:
745
758
  >
@@ -749,7 +762,7 @@ If the SOQL probe returned "not supported", send this message verbatim to the op
749
762
  > 2. Wait for the confirmation banner.
750
763
  > 3. Reply `done` here and I'll continue.
751
764
 
752
- When the operator replies `done`, re-run the F.4.0 SOQL probe. If it still returns "not supported", either the toggle wasn't flipped or there's an org-shape gap. Ask them to double-check, then re-probe. If the org genuinely does not have this surface (older-API pod), fall through to the F.4 skip: continue the deploy without the Orchestrator, and log the gap in the operator handoff.
765
+ If the org cannot enable this at all, skip §F.4 entirely and log the gap in the operator handoff.
753
766
 
754
767
  **F.4.2 — Bot prerequisite check.**
755
768
 
@@ -1419,7 +1432,7 @@ sf org open --target-org $ORG --path "/<url-path>/"
1419
1432
  | Guest curl to `/webruntime/api/.../connect/self-service/*` returns `HTTP 401 UNAUTHORIZED` | `Network.OptionsGuestChatterEnabled = False` on target | Run §L.0 diagnostic; PATCH to True via Data API — no republish required |
1420
1433
  | Portal loads, `/concierge/config` returns 200, prompt bar renders but says **"Agents are not available. Try again later."** | Bot-routing wiring missing. Typical gaps: `PresenceUserConfigUser`, `GroupMember`, `Group.QueueRoutingConfigId` | Run §N — three Data-API writes |
1421
1434
  | §N writes all succeed but chat *still* shows "Agents are not available" immediately | Automated-Process pickup path needs to spin up after routing wiring changes | Re-activate the BotVersion via CLI (`sf agent deactivate` + `sf agent activate`). Wait a few minutes. Do NOT rely on `UserServicePresence` COUNT as the readiness signal |
1422
- | Prompt bar responses are ungrounded / navigate to `/error` on newer-API orgs | Agentforce Orchestrator not wired | See §F.4 — enable the OrgPerm (operator pause) then `POST /connect/self-service/setup/agentOrchestrator` |
1435
+ | Prompt bar responses are ungrounded / navigate to `/error` on newer-API orgs | Agentforce Orchestrator not wired | See §F.4 — `POST /connect/self-service/setup/agentforceOrchestratorEnablement?includeAgentforceOrchestratorPerms=false`, verify SOQL probe, then `POST /connect/self-service/setup/agentOrchestrator` |
1423
1436
  | Agentforce Orchestrator's "Add Tool → Agent" picker is empty | `BotDefinition.AgentTemplate` is `null` — Bot was created via headless Metadata deploy, not the Setup wizard | Delete the Bot and recreate via Setup → Agentforce Agents → New Agent → From Template → Agentforce Service Agent |
1424
1437
 
1425
1438
  ---
@@ -28,9 +28,18 @@ Creates `MessagingChannel` metadata XML for Salesforce Messaging for In-App and
28
28
 
29
29
  ---
30
30
 
31
+ ## Entry points and transport
32
+
33
+ - **Delegated** (`context.calledFrom == "service-onboarding"`): use the approved `values`, ask nothing, and return `failed` naming any missing required key. **Standalone** (no `context`): run Clarifying Questions.
34
+ - **Transport A** (primary): headless-360 MCP `dispatch` / `dispatch_readonly` on `/headless/metadata`. **Transport B**: the `sf` commands below, only where a probe shows Transport A unavailable.
35
+
36
+ Details: `references/onboarding_contract.md` (§1 entry points, §2 transport, §3 pre-scan, §5 result).
37
+
38
+ ---
39
+
31
40
  ## Clarifying Questions
32
41
 
33
- Before generating, ask the user if not already clear:
42
+ Before generating, ask the user if not already clear (standalone only):
34
43
 
35
44
  - What is the channel name / label? (used for `masterLabel` and file name)
36
45
  - What routing type? (Omni-Channel Flow, Omni-Channel Queue, User, or Agentforce Service Agent)
@@ -80,19 +89,21 @@ All steps are sequential. Do not skip or reorder.
80
89
 
81
90
  1. **Verify org API version** — run `scripts/check-api-version.sh 67.0 <org-alias>` and report any errors it returns. If the script fails, generate a `sfdx-project.json` in the metadata output folder with `"sourceApiVersion": "67.0"`.
82
91
 
83
- 2. **Collect inputs** — confirm the channel label, routing type, routing target, and verification settings from the user per Clarifying Questions above.
92
+ 2. **Collect inputs** — standalone: per Clarifying Questions. Delegated: from `values`. Then probe transport with a read-only `GET` of `MessagingChannel` (contract §2).
84
93
 
85
94
  3. **Determine file name** — run `scripts/normalize-channel-name.sh "<LABEL>"` and surface any errors it returns.
86
95
 
87
- 4. **Verify routing target exists** — query the org to confirm the referenced routing target exists:
96
+ 3a. **Pre-scan** — `GET` `MessagingChannel` by the step 3 name (contract §3): none → create; matches and active → no write, `already-configured`, skip to Phase 4; matches but inactive → activate only (step 15); differs → announce each change, then write the full document (standalone: ask first).
97
+
98
+ 4. **Verify routing target exists** — skip when step 3a ended in `already-configured` or activate-only, so a re-run never creates a queue. Query the org to confirm the referenced routing target exists:
88
99
  - For Queue: `sf data query --query "SELECT Id, DeveloperName FROM Group WHERE Type='Queue' AND DeveloperName='<QUEUE_NAME>'" --target-org <org-alias>`
89
100
  - For Flow: `sf data query --query "SELECT Id, ApiName FROM FlowDefinitionView WHERE ApiName='<FLOW_NAME>' AND IsActive=true" --target-org <org-alias>`
90
101
  - For User: `sf data query --query "SELECT Id, Username FROM User WHERE Id='<USER_ID>' AND IsActive=true" --target-org <org-alias>`
91
102
  - For ASA: `sf data query --query "SELECT Id, DeveloperName FROM BotDefinition WHERE DeveloperName='<BOT_NAME>'" --target-org <org-alias>`
92
103
  - Also verify the fallback queue exists (required for Flow, User, and ASA routing)
93
104
 
94
- If any target is not found, inform the user and ask whether to create it. If the user confirms:
95
- - For Queue: generate a `.queue-meta.xml` with `MessagingSession` as the `queueSobject` type and deploy it before the channel. **A new queue is unusable for routing without a `QueueRoutingConfig` — a channel deployed against a queue with none fails at session-start with "Agents are not available. Try again later," even though the channel itself deploys and activates cleanly.** Immediately after the queue deploys, resolve or create its routing config by following `service-agentforce-channel-configure`'s `references/queue-resolution.md` Step 4 — do not defer this to a later skill invocation, since this may be the only place a newly created queue is ever touched.
105
+ If any target is not found: standalone — inform the user and ask whether to create it. Delegated — return `failed` naming the missing target; never create it. If the standalone user confirms creating:
106
+ - For Queue: generate a `.queue-meta.xml` with `MessagingSession` as the `queueSobject` type and deploy it before the channel. **A new queue needs a `QueueRoutingConfig`, or sessions fail with "Agents are not available. Try again later" even though the channel deploys and activates.** Right after the queue deploys, resolve or create one per `service-agentforce-channel-configure`'s `references/queue-resolution.md` Step 4 — do not defer it.
96
107
  - For Flow/User/ASA: inform the user that the flow, user, or bot must be created separately (out of scope for this skill)
97
108
 
98
109
  5. **Read the channel settings reference** — load `references/channel_settings.md` to understand all available configuration options and their valid values.
@@ -110,10 +121,7 @@ All steps are sequential. Do not skip or reorder.
110
121
  | User | `User` | `sessionHandlerUser` + `sessionHandlerQueue` (fallback) |
111
122
  | Agentforce Service Agent | `AgentforceServiceAgent` | `sessionHandlerQueue` (fallback) + `sessionHandlerAsa` (bot dev name — required, see v67 note below) |
112
123
 
113
- > **v67 note — `<sessionHandlerAsa>` is required in the XML, not rejected.** Confirmed on a v67.0 org: omitting `<sessionHandlerAsa>` causes the deploy to fail with "Missing required Agentforce Service Agent." Include it and the deploy binds `SessionHandlerId` automatically — no post-deploy Data API PATCH needed.
114
- > 1. **Verify the bot is Active before channel creation.** The Metadata API rejects binding with "Only active Agentforce Service Agents are supported." Run `sf agent activate -o <org> --api-name <BotDevName>` and confirm `BotVersion.Status = Active` first.
115
- > 2. Deploy the XML with `<sessionHandlerType>AgentforceServiceAgent</sessionHandlerType>`, `<sessionHandlerQueue>` (fallback), and `<sessionHandlerAsa>{BotDevName}</sessionHandlerAsa>`.
116
- > 3. Verify: `sf data query -o <org> -q "SELECT SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE DeveloperName='<ChannelDevName>'" --json` — both must be non-null after the deploy, with no separate PATCH step.
124
+ > **v67 note — `<sessionHandlerAsa>` is required** (omitting it fails with "Missing required Agentforce Service Agent"); the deploy then binds `SessionHandlerId`, no PATCH needed. The bot must be Active first: run `sf agent activate -o <org> --api-name <BotDevName>` and confirm `BotVersion.Status = Active`.
117
125
 
118
126
  8. **Apply user verification** — if enabled, set `embeddedConfig.authMode` to `Auth` and include `<messagingAuthorizations>`. If not enabled, set `embeddedConfig.authMode` to `UnAuth` and omit `<messagingAuthorizations>`.
119
127
 
@@ -142,21 +150,21 @@ All steps are sequential. Do not skip or reorder.
142
150
 
143
151
  ### Phase 3 — Deploy and Activate
144
152
 
145
- 14. **Deploy the channel** — deploy the generated `.messagingChannel-meta.xml` file to the target org:
153
+ 14. **Write the channel** — Transport A: `POST` to create, `PUT` the **full** document to update (a minimal PUT drops `embeddedConfig`); check `results[].success`, then re-`GET`. Transport B:
146
154
  ```bash
147
155
  sf project deploy start --source-dir <path-to-messagingChannels-folder> --target-org <org-alias>
148
156
  ```
149
157
 
150
- 15a. **ASA routing only — verify the bind landed.** Skip this step for Queue, Flow, and User routing types. Because `<sessionHandlerAsa>` was included in the deployed XML (step 7), the deploy itself binds `SessionHandlerId` — no separate Data API PATCH is needed.
158
+ 15a. **ASA routing only — verify the bind landed** (skip for Queue, Flow, User):
151
159
 
152
160
  ```bash
153
161
  sf data query -o <org> --json \
154
162
  -q "SELECT SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE DeveloperName='<CHANNEL_DEV_NAME>'"
155
163
  ```
156
164
 
157
- Both `SessionHandlerId` and `FallbackQueueId` must be non-null. If either is null, confirm `<sessionHandlerAsa>` and `<sessionHandlerQueue>` were both present in the deployed XML and that the bot was Active before the deploy.
165
+ Both must be non-null; if not, check both handler elements were in the XML and the bot was Active before the deploy.
158
166
 
159
- 15. **Activate the channel** — after successful deployment, activate the messaging channel:
167
+ 15. **Activate the channel** — after a successful write, activate the messaging channel (`IsActive` is not part of the metadata). Transport A: sObject update `{"IsActive": true}` through `dispatch`. Transport B:
160
168
  ```bash
161
169
  sf data update record --sobject MessagingChannel --where "DeveloperName='<CHANNEL_NAME>'" --values "IsActive=true" --target-org <org-alias>
162
170
  ```
@@ -165,7 +173,9 @@ All steps are sequential. Do not skip or reorder.
165
173
 
166
174
  16. **Verify against checklist** — confirm all items in the Verification Checklist below pass before presenting output.
167
175
 
168
- 17. **Present output** — show the generated file to the user with a summary of configured settings and confirm activation status. Offer next steps:
176
+ 17. **Report** — delegated: return the contract §5 result and stop. Standalone: continue.
177
+
178
+ 18. **Present output** (standalone) — show the generated file to the user with a summary of configured settings and confirm activation status. Offer next steps:
169
179
  - **Automated responses** — ask if the user wants to configure `<automatedResponses>` (OptOutConfirmation, HelpResponse). If yes, generate elements with `autoResponseContentType: TextResponse`, `language`, and XML-escaped `response` text, then redeploy.
170
180
 
171
181
  ---
@@ -278,6 +288,7 @@ File structure follows the template in `assets/messaging_channel_template.xml`.
278
288
 
279
289
  | File | When to read |
280
290
  |------|-------------|
291
+ | `references/onboarding_contract.md` | Delegated mode (`context.calledFrom == "service-onboarding"`), org writes (transport), module registration |
281
292
  | `assets/messaging_channel_template.xml` | Before generating — use as the starting structure |
282
293
  | `references/channel_settings.md` | When configuring channel options beyond defaults |
283
294
  | `scripts/check-api-version.sh` | Phase 1 — verify org API version meets the passed minimum (67.0) |
@@ -0,0 +1,143 @@
1
+ # Onboarding contract — delegated mode, transport, pre-scan, registration
2
+
3
+ Read this when the invocation carries a `context` object, before any org write, or when registering the skill in an
4
+ onboarding module. It implements the Feature Setup Skill Guidelines (Conversational Onboarding, Service Cloud) for
5
+ this skill. The sibling `service-digital-engagement-deployment-configure` has the same structure.
6
+
7
+ ## 1. Two entry points, one write procedure
8
+
9
+ | | Delegated | Standalone |
10
+ |---|---|---|
11
+ | Detected by | `context.calledFrom == "service-onboarding"` | no `context` object |
12
+ | Values come from | `values` in the payload — already approved in the plan table | you ask, using the defaults in SKILL.md |
13
+ | Questions | none. A missing required key is a caller bug: stop and return `failed` naming the key. Never default a value the admin did not approve | the Clarifying Questions in SKILL.md |
14
+ | Wording | `discoveryMode == "guided"` → plain business language (§4). `"pickAreas"` → Salesforce terminology | Salesforce terminology (the Clarifying Questions as written) |
15
+ | Ends with | the structured result in section 5 | a plain-language summary of the same outcome |
16
+
17
+ Delegated payload:
18
+
19
+ ```json
20
+ {
21
+ "values": {
22
+ "channelLabel": "Acme Support Chat",
23
+ "routingType": "Queue",
24
+ "routingTarget": "Support_Queue",
25
+ "fallbackQueue": null,
26
+ "userVerification": true
27
+ },
28
+ "context": { "calledFrom": "service-onboarding", "discoveryMode": "guided" }
29
+ }
30
+ ```
31
+
32
+ Required `values`: `channelLabel`, `routingType`, `routingTarget`; `fallbackQueue` is required when `routingType` is
33
+ `Flow`, `User` or `AgentforceServiceAgent`. `userVerification` defaults to `true`, matching SKILL.md. Every other
34
+ channel setting uses the SKILL.md defaults unless a key is supplied.
35
+
36
+ Missing routing target, delegated: return `failed`, `errorMessage` = "<Flow|User|Bot|Queue> <name> not found". Never
37
+ create one — queues come from the `queues` feature this one depends on.
38
+
39
+ ## 2. Transport — `/headless/metadata` first, `sf` CLI as fallback
40
+
41
+ **Transport A (primary): the headless-360 MCP `dispatch` / `dispatch_readonly` tools** against the synchronous
42
+ Metadata-CRUD route, fully-qualified: `/services/data/vXX.0/headless/metadata/<Type>?type=<Type>&fullName=<Name>`.
43
+
44
+ - `GET` = read (`dispatch_readonly`) → `{type, fullName, xmlRep}`. `POST` = create-only; re-POSTing an existing
45
+ `fullName` returns `DUPLICATE_VALUE`. `PUT` = update. On writes the body must carry `type`, `fullName` **and** `xmlRep`.
46
+ - Response is synchronous: `{results:[{success, errors[], ...}]}`. HTTP 200 can still be a failure — inner `success` wins.
47
+ - **Create once, then update.** Never re-run `POST` for a later change.
48
+ - **A minimal PUT silently drops inline blocks.** On `MessagingChannel`, a PUT that omitted `embeddedConfig` cleared
49
+ it while `automatedResponses` and `messagingKeywords` survived. Always GET → modify → PUT the **full** document.
50
+ Re-GET after the PUT and diff.
51
+ - Activation is not metadata: `IsActive` is absent from the `MessagingChannel` representation. Activate with an
52
+ sObject update (`{"IsActive": true}`) through `dispatch`, or `sf data update record` on Transport B.
53
+ - Use the same `dispatch` tool for the probe and the write. `sf api request rest` / `curl` carry the `sf` CLI's separate
54
+ credential, which can be expired while the MCP token is valid — a REST URL called through the CLI is **not** Transport A.
55
+ - Metadata references Flow/Queue by **API name** (`sessionHandlerFlow`, `sessionHandlerQueue`), not by runtime Id.
56
+
57
+ **Probe per type, per operation — never infer from another type.** Run a read-only `GET` for each type you are about
58
+ to touch (`MessagingChannel`; and `Queue` if you must create one).
59
+
60
+ | Probe result | Meaning | Action |
61
+ |---|---|---|
62
+ | `xmlRep` returned, or "Metadata not found" style error | type is onboarded | Transport A |
63
+ | `UNSUPPORTED_OPERATION` for the type or verb | not onboarded for that verb | Transport B for that call only |
64
+ | auth / connection error | MCP unavailable | Transport B; say so |
65
+
66
+ **Transport B (fallback): `sf` CLI** — `sf project deploy start` and `sf data query` / `sf data update record` as in
67
+ SKILL.md. Use only when the probe says Transport A is unavailable for that type/verb.
68
+
69
+ Routing-target existence checks (Queue, Flow, User, Bot) are SOQL: `dispatch_readonly` on the REST query endpoint first; `sf data query` only if that call returns a path/route error.
70
+
71
+ ## 3. Pre-scan (idempotency)
72
+
73
+ Run before any write, both modes. `GET` `MessagingChannel` by `fullName` = the normalized channel name.
74
+
75
+ | Finding | Outcome |
76
+ |---|---|
77
+ | No such channel | not set up → create (`POST`) |
78
+ | Exists, and every targeted setting equals the target (routing type and target, fallback queue, `authMode`, plus each supplied key) **and** `IsActive = true` | **already configured** — no write, return `already-configured` |
79
+ | Exists, matches, but `IsActive = false` | only activate; do not rewrite the metadata |
80
+ | Exists, some targeted setting differs | **drift** — announce each change ("routing: Queue A → Queue B"), then write the full document. Standalone: ask for a yes first. Delegated: the plan approval covers it |
81
+ | Exists but was created by Connect `channel/setup` (defaults populated: opt-out/help keywords, `embeddedConfig`) | you now own the whole document: GET, modify only targeted fields, PUT the full result |
82
+
83
+ Standalone, channel exists: offer exactly three choices — no change / override / add to existing.
84
+
85
+ ## 4. Questions in the admin's language (guided mode)
86
+
87
+ Ask about the outcome; state the default with each; keep it to a handful.
88
+
89
+ | Instead of | Ask |
90
+ |---|---|
91
+ | "Routing type: Queue, Flow, User or Agentforce Service Agent?" | "When a customer starts a chat, who should get it first: a team (queue), a specific person, or your AI agent? (Default: a team.)" |
92
+ | "Fallback queue developer name?" | "If that person or agent isn't available, which team should the chat go to?" |
93
+ | "Enable User Verification (JWT)?" | "Will customers be signed in to your site when they chat, so you know who they are? (Default: yes.)" Yes → Auth mode; setting up the signing keys is a separate step |
94
+ | "Pre-chat form / custom parameters" | "What should we ask customers before the chat starts? (Default: name and email.)" |
95
+ | "`isAbandonedChatsEnabled`, `chatAbandonmentTimeout`…" | Not asked; use the SKILL.md defaults |
96
+
97
+ ## 5. Result returned to the coordinator
98
+
99
+ ```json
100
+ {
101
+ "status": "success | already-configured | failed",
102
+ "errorMessage": "<empty unless failed>",
103
+ "outputs": {
104
+ "channelName": "Acme_Support_Chat",
105
+ "messagingChannelId": "<id or null>",
106
+ "isActive": true
107
+ }
108
+ }
109
+ ```
110
+
111
+ `status` and `errorMessage` are the contract. `outputs` is always returned; it is an addition to the documented shape.
112
+ On `failed`: `errorMessage` names the failed step and the platform error; stop; do not retry.
113
+
114
+ ## 6. Registration entry (draft for the onboarding team to place in a module)
115
+
116
+ ```yaml
117
+ - id: messagingChannel
118
+ title: "Customer messaging channel"
119
+ description: "Create and switch on the channel that receives customer chats from your website or app, and decide who answers first (a team, a person, or an AI agent)."
120
+ delegateSkill: service-digital-engagement-channel-configure
121
+ dependsOn: [queues]
122
+ revisionOwner: service-digital-engagement-channel-configure
123
+ # org perms: confirm exact names with Digital Engagement licensing (not yet confirmed)
124
+ values:
125
+ channelLabel: { from: signal.messagingChannelName, default: "<company> Support Chat" }
126
+ routingType: { from: signal.messagingRouting, default: "Queue" }
127
+ routingTarget: { from: signal.messagingRoutingTarget }
128
+ fallbackQueue: { from: signal.messagingFallbackQueue }
129
+ userVerification: { from: signal.messagingSignedIn, default: true }
130
+ writes: "MessagingChannel (Metadata CRUD); IsActive via sObject update"
131
+ gating:
132
+ irreversible: false
133
+ note: "Reversible by deactivating the channel. Pre-chat form fields can be deactivated but the form cannot be fully deleted; removing a channel custom parameter needs the form field removed and the deployment republished first."
134
+ validate:
135
+ check: check-messaging-channel
136
+ passWhen: "GET MessagingChannel fullName=<normalized name> returns the targeted sessionHandlerType and target and the intended authMode; SOQL SELECT IsActive, SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE DeveloperName=<name> returns IsActive=true, and non-null SessionHandlerId/FallbackQueueId where the routing type requires them"
137
+ alternative: "Setup > Messaging Settings > New Channel > Messaging for In-App and Web; choose routing, then activate."
138
+ ```
139
+
140
+ Open items for the onboarding team: the `signal.*` keys do not exist in `onboarding-state.schema.json` yet
141
+ (`channels` allows only email/web); `userVerification: true` makes an Auth channel, which also needs the
142
+ user-verification step before signed-in customers can chat — decide if that is its own feature; the coordinator must
143
+ accept `outputs`; org perm names are not yet confirmed.