@salesforce/afv-skills 1.55.0 → 1.56.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 (220) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-generate/SKILL.md +8 -0
  3. package/skills/agentforce-generate/references/actions-reference.md +2 -2
  4. package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
  5. package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
  6. package/skills/agentforce-observe/SKILL.md +13 -9
  7. package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
  8. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
  9. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
  10. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
  11. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
  12. package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
  13. package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
  14. package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
  15. package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
  16. package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
  17. package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
  18. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
  19. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
  20. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
  21. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
  22. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
  23. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
  24. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
  25. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
  26. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
  27. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
  28. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
  29. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
  30. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
  31. package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
  32. package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
  33. package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
  34. package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
  35. package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
  36. package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
  37. package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
  38. package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
  39. package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
  40. package/skills/dx-org-analyze/SKILL.md +0 -1
  41. package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
  42. package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
  43. package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
  44. package/skills/dx-org-manage/SKILL.md +6 -50
  45. package/skills/dx-org-manage/examples/README.md +10 -16
  46. package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
  47. package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
  48. package/skills/dx-org-shape-manage/SKILL.md +4 -3
  49. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
  50. package/skills/experience-accessibility-validate/SKILL.md +8 -5
  51. package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
  52. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
  53. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
  54. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
  55. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
  56. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
  57. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
  58. package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
  59. package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
  60. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
  61. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
  62. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
  63. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
  64. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
  65. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
  66. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
  67. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
  68. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
  69. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
  70. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
  71. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
  72. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
  73. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
  74. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
  75. package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
  76. package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
  77. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
  78. package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
  79. package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
  80. package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
  81. package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
  82. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
  83. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
  84. package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
  85. package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
  86. package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
  87. package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
  88. package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
  89. package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
  90. package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
  91. package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
  92. package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
  93. package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
  94. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
  95. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
  96. package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
  97. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
  98. package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
  99. package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
  100. package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
  101. package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
  102. package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
  103. package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
  104. package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
  105. package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
  106. package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
  107. package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
  108. package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
  109. package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
  110. package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
  111. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
  112. package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
  113. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
  114. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
  115. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
  116. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
  117. package/skills/mobile-apps-create/SKILL.md +1 -1
  118. package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
  119. package/skills/platform-sandbox-configure/SKILL.md +108 -133
  120. package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
  121. package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
  122. package/skills/platform-widget-generate/SKILL.md +26 -18
  123. package/skills/platform-widget-generate/examples/formulas.json +191 -0
  124. package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
  125. package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
  126. package/skills/sales-call-scoring-configure/SKILL.md +318 -0
  127. package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
  128. package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
  129. package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
  130. package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
  131. package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
  132. package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
  133. package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
  134. package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
  135. package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
  136. package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
  137. package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
  138. package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
  139. package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
  140. package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
  141. package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
  142. package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
  143. package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
  144. package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
  145. package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
  146. package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
  147. package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
  148. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
  149. package/skills/service-de-channel-activate/SKILL.md +6 -5
  150. package/skills/service-de-channel-create/SKILL.md +3 -3
  151. package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
  152. package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
  153. package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
  154. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
  155. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
  156. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
  157. package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
  158. package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
  159. package/skills/service-helpagent-coordinate/SKILL.md +3 -2
  160. package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
  161. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
  162. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
  163. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
  164. package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
  165. package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
  166. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
  167. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
  168. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
  169. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
  170. package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
  171. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
  172. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
  173. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
  174. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
  175. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
  176. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
  177. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
  178. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
  179. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
  180. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
  181. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
  182. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
  183. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
  184. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
  185. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
  186. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
  187. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
  188. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
  189. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
  190. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
  191. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
  192. package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
  193. package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
  194. package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
  195. package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
  196. package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
  197. package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
  198. package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
  199. package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
  200. package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
  201. package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
  202. package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
  203. package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
  204. package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
  205. package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
  206. package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
  207. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
  208. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
  209. package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
  210. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  211. package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
  212. package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
  213. package/skills/dx-org-manage/references/cli_flags.md +0 -67
  214. package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
  215. package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
  216. package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
  217. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
  218. package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
  219. /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
  220. /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
@@ -0,0 +1,190 @@
1
+ #!/bin/bash
2
+ # sales_call_scoring: story="custom-competency-crud" owner="W-CUSTOM-CRUD"
3
+ #
4
+ # Edit a Coaching Competency's MasterLabel, Description, and/or
5
+ # EvaluationInstructions.
6
+ # Preserves Id, DeveloperName, IsActive, EvaluationScope, SourceCompetencyTemplate.
7
+ #
8
+ # --label renames the admin-facing display name (MasterLabel). DeveloperName
9
+ # is NOT renamed — it's the API name used by any downstream references and
10
+ # would risk breaking integrations to change.
11
+ #
12
+ # Companion of create-custom-competency.sh. Kept as its own file so the
13
+ # custom-CRUD story can extend it (e.g. accept scope change to
14
+ # SpecificCallsOnly + EvaluationConditions) without conflicting with the
15
+ # OOTB story's edit surface.
16
+ #
17
+ # Usage:
18
+ # ./edit-custom-competency.sh <org-alias> <name-or-id> --label "<new-label>"
19
+ # ./edit-custom-competency.sh <org-alias> <name-or-id> --instructions-file <file>
20
+ # ./edit-custom-competency.sh <org-alias> <name-or-id> --description "<new-desc>"
21
+ # ./edit-custom-competency.sh <org-alias> <name-or-id> --instructions "<literal>"
22
+ # (combine flags to update multiple fields at once)
23
+
24
+ set -euo pipefail
25
+
26
+ export NO_COLOR=1
27
+ export FORCE_COLOR=0
28
+
29
+ ORG_ALIAS="${1:-}"
30
+ IDENT="${2:-}"
31
+ shift 2 || true
32
+
33
+ if [[ -z "$ORG_ALIAS" || -z "$IDENT" ]]; then
34
+ echo "Usage: $0 <org-alias> <name-or-id> [--label \"<text>\"] [--instructions-file <file>] [--description \"<text>\"] [--instructions \"<text>\"]"
35
+ exit 1
36
+ fi
37
+
38
+ NEW_LABEL=""
39
+ NEW_INSTRUCTIONS=""
40
+ NEW_DESCRIPTION=""
41
+
42
+ while [[ $# -gt 0 ]]; do
43
+ case "$1" in
44
+ --label)
45
+ NEW_LABEL="$2"
46
+ shift 2
47
+ ;;
48
+ --instructions-file)
49
+ if [[ ! -f "$2" ]]; then
50
+ echo "File not found: $2"; exit 1
51
+ fi
52
+ NEW_INSTRUCTIONS=$(cat "$2")
53
+ shift 2
54
+ ;;
55
+ --instructions)
56
+ NEW_INSTRUCTIONS="$2"
57
+ shift 2
58
+ ;;
59
+ --description)
60
+ NEW_DESCRIPTION="$2"
61
+ shift 2
62
+ ;;
63
+ *)
64
+ echo "Error: Unknown flag: $1"; exit 1
65
+ ;;
66
+ esac
67
+ done
68
+
69
+ if [[ -z "$NEW_LABEL" && -z "$NEW_INSTRUCTIONS" && -z "$NEW_DESCRIPTION" ]]; then
70
+ echo "Nothing to update — pass --label, --instructions[-file], and/or --description"
71
+ exit 1
72
+ fi
73
+
74
+ if [[ -n "$NEW_LABEL" ]]; then
75
+ LEN=${#NEW_LABEL}
76
+ # MasterLabel is a standard platform label field, capped at 80 chars.
77
+ if [[ "$LEN" -gt 80 ]]; then
78
+ echo "MasterLabel is ${LEN} chars — max is 80."
79
+ exit 1
80
+ fi
81
+ fi
82
+
83
+ if [[ -n "$NEW_INSTRUCTIONS" ]]; then
84
+ LEN=${#NEW_INSTRUCTIONS}
85
+ if [[ "$LEN" -gt 16000 ]]; then
86
+ echo "EvaluationInstructions is ${LEN} chars — max is 16000."
87
+ exit 1
88
+ fi
89
+ fi
90
+
91
+ if [[ -n "$NEW_DESCRIPTION" ]]; then
92
+ LEN=${#NEW_DESCRIPTION}
93
+ if [[ "$LEN" -gt 255 ]]; then
94
+ echo "Description is ${LEN} chars — max is 255."
95
+ exit 1
96
+ fi
97
+ fi
98
+
99
+ # ---------------------------------------------------------------------------
100
+ # Auth
101
+ # ---------------------------------------------------------------------------
102
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
103
+ # shellcheck source=shared/auth.sh
104
+ source "$SCRIPT_DIR/shared/auth.sh"
105
+ # shellcheck source=shared/soql.sh
106
+ source "$SCRIPT_DIR/shared/soql.sh"
107
+
108
+ resolve_org_auth "$ORG_ALIAS" || exit 1
109
+
110
+ TOOLING_BASE="services/data/v68.0/tooling"
111
+
112
+ # ---------------------------------------------------------------------------
113
+ # Resolve identifier
114
+ # ---------------------------------------------------------------------------
115
+ if [[ "$IDENT" =~ ^1nA[0-9a-zA-Z]{12,15}$ ]]; then
116
+ WHERE="Id='${IDENT}'"
117
+ else
118
+ SAFE=$(soql_escape "$IDENT")
119
+ WHERE="DeveloperName='${SAFE}' OR MasterLabel='${SAFE}'"
120
+ fi
121
+ QUERY_ENC=$(printf 'SELECT Id,MasterLabel,IsActive FROM EnablementCompetencyDef WHERE %s' "$WHERE" | jq -sRr @uri)
122
+ LOOKUP=$(sf api request rest "${TOOLING_BASE}/query/?q=${QUERY_ENC}" --target-org "$ORG_ALIAS" 2>/dev/null || true)
123
+ RC=$(echo "$LOOKUP" | jq -r '.totalSize // 0' 2>/dev/null || echo "0")
124
+
125
+ if [[ "$RC" == "0" ]]; then echo "No competency matched: $IDENT"; exit 1; fi
126
+ if [[ "$RC" != "1" ]]; then
127
+ echo "Multiple competencies matched '$IDENT' — refusing to bulk-edit."
128
+ echo "$LOOKUP" | jq -r '.records[] | " \(.Id) \(.MasterLabel)"'
129
+ exit 1
130
+ fi
131
+
132
+ REC_ID=$(echo "$LOOKUP" | jq -r '.records[0].Id')
133
+ REC_LABEL=$(echo "$LOOKUP" | jq -r '.records[0].MasterLabel')
134
+ REC_ACTIVE=$(echo "$LOOKUP" | jq -r '.records[0].IsActive')
135
+
136
+ echo "$REC_LABEL ($REC_ID) — IsActive=$REC_ACTIVE"
137
+
138
+ # ---------------------------------------------------------------------------
139
+ # Uniqueness gate for rename — mirrors the check create-custom-competency.sh
140
+ # runs on create. Excludes the target record's own Id so a no-op rename
141
+ # doesn't self-collide.
142
+ # ---------------------------------------------------------------------------
143
+ if [[ -n "$NEW_LABEL" && "$NEW_LABEL" != "$REC_LABEL" ]]; then
144
+ LABEL_ENC=$(printf "SELECT Id FROM EnablementCompetencyDef WHERE MasterLabel='%s' AND Id!='%s'" \
145
+ "$(soql_escape "$NEW_LABEL")" "$REC_ID" | jq -sRr @uri)
146
+ DUP=$( (sf api request rest "${TOOLING_BASE}/query/?q=${LABEL_ENC}" --target-org "$ORG_ALIAS" 2>/dev/null || true) \
147
+ | jq -r '.totalSize // 0' 2>/dev/null)
148
+ DUP="${DUP:-0}"
149
+ if [[ "$DUP" != "0" ]]; then
150
+ echo "Another competency already uses label '$NEW_LABEL' — refusing to duplicate."
151
+ echo " Choose a different label, or edit the existing record instead."
152
+ exit 1
153
+ fi
154
+ fi
155
+
156
+ # ---------------------------------------------------------------------------
157
+ # Build PATCH payload
158
+ # ---------------------------------------------------------------------------
159
+ PAYLOAD='{}'
160
+ if [[ -n "$NEW_LABEL" ]]; then
161
+ PAYLOAD=$(echo "$PAYLOAD" | jq --arg v "$NEW_LABEL" '. + {MasterLabel: $v}')
162
+ fi
163
+ if [[ -n "$NEW_INSTRUCTIONS" ]]; then
164
+ PAYLOAD=$(echo "$PAYLOAD" | jq --arg v "$NEW_INSTRUCTIONS" '. + {EvaluationInstructions: $v}')
165
+ fi
166
+ if [[ -n "$NEW_DESCRIPTION" ]]; then
167
+ PAYLOAD=$(echo "$PAYLOAD" | jq --arg v "$NEW_DESCRIPTION" '. + {Description: $v}')
168
+ fi
169
+
170
+ # sf api request rest exits non-zero on HTTP failure; success on Tooling
171
+ # PATCH is HTTP 204 (empty body). Capture both stdout and exit status.
172
+ if RESP_BODY=$(sf api request rest "${TOOLING_BASE}/sobjects/EnablementCompetencyDef/${REC_ID}" \
173
+ --target-org "$ORG_ALIAS" \
174
+ --method PATCH \
175
+ --header 'Content-Type: application/json' \
176
+ --body "$PAYLOAD" \
177
+ 2>&1); then
178
+ if [[ -n "$NEW_LABEL" && "$NEW_LABEL" != "$REC_LABEL" ]]; then
179
+ echo "Renamed '$REC_LABEL' → '$NEW_LABEL'"
180
+ else
181
+ echo "Updated $REC_LABEL"
182
+ fi
183
+ [[ -n "$NEW_INSTRUCTIONS" ]] && echo " EvaluationInstructions: ${#NEW_INSTRUCTIONS} chars"
184
+ [[ -n "$NEW_DESCRIPTION" ]] && echo " Description: ${#NEW_DESCRIPTION} chars"
185
+ exit 0
186
+ else
187
+ echo "Error: PATCH failed"
188
+ echo " $RESP_BODY"
189
+ exit 1
190
+ fi
@@ -0,0 +1,436 @@
1
+ #!/bin/bash
2
+
3
+ # Enable Call Coaching (a.k.a. AI Call Scoring / Coaching Competencies) on a
4
+ # Salesforce org via SOAP Metadata API.
5
+ #
6
+ # Handles the PRD-scoped org states for the ECI arm:
7
+ # 1. Not available → refuse with PRD-verbatim message.
8
+ # 2. Available, not enabled → enable Einstein GenAI if off, flip
9
+ # enableECICallScoring.
10
+ # 3. Enabled, no competencies → exit 0 with "ready for competency setup".
11
+ # 4. Enabled, competencies exist → exit 0 with "already enabled, N competencies".
12
+ #
13
+ # River Rush (Momentum) arm detection is deferred until that arm goes live —
14
+ # see SKILL.md. Re-add it as a 5th state / advisory when it does.
15
+ #
16
+ # Usage: ./enable-call-scoring.sh <org-alias>
17
+
18
+ set -euo pipefail
19
+
20
+ export NO_COLOR=1
21
+ export FORCE_COLOR=0
22
+
23
+ ORG_ALIAS="${1:-}"
24
+
25
+ if [[ -z "$ORG_ALIAS" ]]; then
26
+ echo "Error: Missing org alias"
27
+ echo "Usage: $0 <org-alias>"
28
+ exit 1
29
+ fi
30
+
31
+ echo "Enabling Call Coaching for org: $ORG_ALIAS"
32
+ echo ""
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # Auth
36
+ # ---------------------------------------------------------------------------
37
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
38
+ # shellcheck source=shared/auth.sh
39
+ source "$SCRIPT_DIR/shared/auth.sh"
40
+
41
+ # SOAP Metadata API embeds the session token in the request body (see
42
+ # soap_read below). This is the only caller in the skill that still needs
43
+ # the raw ACCESS_TOKEN in shell scope — everything else goes through
44
+ # `sf api request rest`. See shared/auth.sh header for the rationale.
45
+ resolve_org_auth_with_token "$ORG_ALIAS" || exit 1
46
+
47
+ # Deliberately no "Org ID: ..." / "Instance URL: ..." banner here — those are
48
+ # admin-comms leaks (rule #5). The org alias is already the admin's mental
49
+ # handle; the record Id and pod URL belong in --verbose debug output only.
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Phase 0: ECI purchase gate — ConversationPilot OrgPermission
53
+ # ---------------------------------------------------------------------------
54
+ # ConversationPilot reflects whether the org is licensed for Einstein
55
+ # Conversation Insights at all, independent of whether an admin has flipped
56
+ # enableCallCoaching on in Setup. Checking it first lets us tell "you don't
57
+ # have ECI, go buy it" apart from "you have ECI, just turn it on" instead of
58
+ # inferring a license gap from a readMetadata fault on enableCallCoaching.
59
+ #
60
+ # Some org shapes (older sandboxes, non-EinsteinConversationInsights-provisioned
61
+ # orgs) reject the OrganizationSettingsDetail query with INVALID_TYPE. That's
62
+ # not a licensing signal — it's a metadata-availability signal. Treat any
63
+ # non-conclusive result as "unknown, defer to SOAP" and only refuse when the
64
+ # query explicitly comes back "false" (W-24022554).
65
+ CONVERSATIONPILOT_ENABLED=""
66
+ if CONVERSATIONPILOT_RAW=$(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
67
+ --query "SELECT SettingValue FROM OrganizationSettingsDetail WHERE SettingName='ConversationPilot' AND DataType='OrgPermissionSettings'" \
68
+ --json 2>/dev/null); then
69
+ CONVERSATIONPILOT_JSON=$(echo "$CONVERSATIONPILOT_RAW" | awk '/^{/,EOF')
70
+ if echo "$CONVERSATIONPILOT_JSON" | jq -e '.status == 0' >/dev/null 2>&1; then
71
+ CONVERSATIONPILOT_ENABLED=$(echo "$CONVERSATIONPILOT_JSON" | jq -r '.result.records[0].SettingValue // empty')
72
+ fi
73
+ fi
74
+
75
+ if [[ "$CONVERSATIONPILOT_ENABLED" == "false" ]]; then
76
+ echo "Call Coaching couldn't be turned on because this org doesn't have Einstein Conversation"
77
+ echo " Insights, which is required to use Call Coaching. To proceed, an admin can go to Setup →"
78
+ echo " Salesforce Go → Einstein Conversation Insights, or contact their sales representative,"
79
+ echo " to understand how to purchase ECI."
80
+ exit 1
81
+ fi
82
+ # If unknown (query rejected / no row), skip this pre-check and let the SOAP
83
+ # state detection below classify the org. TYPE_UNAVAILABLE on the SOAP read
84
+ # is the authoritative "you need Agentforce for Sales" signal.
85
+
86
+ # enableECICallScoring exposes at v68+ only.
87
+ SOAP_ENDPOINT="${INSTANCE_URL}/services/Soap/m/68.0"
88
+
89
+ # Bounds every curl call below so a hung network call can't stall the run
90
+ # indefinitely. 30s matches the Pipeline reference skill's setup-all.sh
91
+ # CURL_TIMEOUT — the only existing timeout precedent in this repo's
92
+ # SOAP-calling scripts.
93
+ readonly CURL_TIMEOUT=30
94
+
95
+ # ---------------------------------------------------------------------------
96
+ # SOAP helpers
97
+ # ---------------------------------------------------------------------------
98
+ # shellcheck source=shared/soap.sh
99
+ source "$SCRIPT_DIR/shared/soap.sh"
100
+
101
+ soap_read() {
102
+ local settings_type="$1"
103
+ local full_name="$2"
104
+ soap_call "readMetadata" "<?xml version='1.0' encoding='utf-8'?>
105
+ <soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:met='http://soap.sforce.com/2006/04/metadata'>
106
+ <soapenv:Header><met:SessionHeader><met:sessionId>${ACCESS_TOKEN}</met:sessionId></met:SessionHeader></soapenv:Header>
107
+ <soapenv:Body><met:readMetadata><met:type>${settings_type}</met:type><met:fullNames>${full_name}</met:fullNames></met:readMetadata></soapenv:Body>
108
+ </soapenv:Envelope>"
109
+ }
110
+
111
+ # classify_field / normalize_class / describe_failure now live in
112
+ # shared/soap.sh (sourced above) so install-ootb-competencies.sh's SOAP
113
+ # fallback can reuse them without duplicating this logic.
114
+
115
+ # ---------------------------------------------------------------------------
116
+ # Availability detection: read all three settings + count competencies.
117
+ # ---------------------------------------------------------------------------
118
+ echo "Detecting org state..."
119
+ echo ""
120
+
121
+ CI_RAW=$(soap_read "ConversationalIntelligenceSettings" "ConversationalIntelligence")
122
+ ECI_CLASS=$(classify_field "$CI_RAW" "enableCallCoaching")
123
+ CS_CLASS=$(classify_field "$CI_RAW" "enableECICallScoring")
124
+ ECI_ENABLED=$(normalize_class "$ECI_CLASS")
125
+ CALL_SCORING_ENABLED=$(normalize_class "$CS_CLASS")
126
+
127
+ GENAI_RAW=$(soap_read "EinsteinGptSettings" "EinsteinGpt")
128
+ GENAI_CLASS=$(classify_field "$GENAI_RAW" "enableEinsteinGptPlatform")
129
+ GENAI_ENABLED=$(normalize_class "$GENAI_CLASS")
130
+
131
+ # River Rush (Momentum) detection deferred until that arm goes live.
132
+
133
+ # Competency count via Tooling API. (sf may prepend a warning to stdout; strip.)
134
+ COMPETENCY_COUNT=""
135
+ if COMP_RAW=$(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
136
+ --query "SELECT COUNT() FROM EnablementCompetencyDef" --json 2>/dev/null); then
137
+ COMP_JSON=$(echo "$COMP_RAW" | awk '/^{/,EOF')
138
+ COMPETENCY_COUNT=$(echo "$COMP_JSON" | jq -r '.result.totalSize // empty' 2>/dev/null || echo "")
139
+ fi
140
+
141
+ echo " Einstein Conversation Insights: ${ECI_ENABLED:-unknown}"
142
+ echo " Einstein Generative AI Platform: ${GENAI_ENABLED:-unknown}"
143
+ echo " Call Coaching: ${CALL_SCORING_ENABLED:-unknown}"
144
+ echo " Configured competencies: ${COMPETENCY_COUNT:-unknown}"
145
+ echo ""
146
+
147
+ # ---------------------------------------------------------------------------
148
+ # State machine (PRD § "Behavior by org state")
149
+ # ---------------------------------------------------------------------------
150
+
151
+ # State 1a: NOT AVAILABLE — CI settings unreadable. Distinguish *why*: a
152
+ # confirmed licensing gap (TYPE_UNAVAILABLE) is a different admin action than
153
+ # a transient auth/network failure, which should be retried, not reported as
154
+ # "you need Agentforce for Sales."
155
+ if [[ -z "$ECI_ENABLED" ]]; then
156
+ case "$ECI_CLASS" in
157
+ TYPE_UNAVAILABLE:*)
158
+ echo "AI call coaching is not available in this org and they need Agentforce for Sales."
159
+ echo ""
160
+ echo " (This skill covers Einstein Conversation Insights (ECI) orgs only —"
161
+ echo " the org's ECI license appears to be missing.)"
162
+ ;;
163
+ AUTH_ERROR:*)
164
+ echo "Could not check org state — authentication/permission error:"
165
+ echo " ${ECI_CLASS#AUTH_ERROR:}"
166
+ echo ""
167
+ echo " Verify the CLI session is valid (sf org login web --alias $ORG_ALIAS) and re-run."
168
+ ;;
169
+ *)
170
+ echo "Could not reach Salesforce to check org state (network/timeout error)."
171
+ echo " Try again in a moment; if it persists, check the org's availability."
172
+ ;;
173
+ esac
174
+ exit 1
175
+ fi
176
+
177
+ # State 4: ENABLED, competencies exist.
178
+ if [[ "$CALL_SCORING_ENABLED" == "true" && -n "$COMPETENCY_COUNT" && "$COMPETENCY_COUNT" -gt 0 ]]; then
179
+ echo "Call Coaching is already enabled — $COMPETENCY_COUNT competency record(s) exist."
180
+ echo " Ready for edit / deactivate / create-new / improvement-suggestion flows."
181
+ echo ""
182
+ echo "Next: $SCRIPT_DIR/list-competencies.sh \"$ORG_ALIAS\""
183
+ exit 0
184
+ fi
185
+
186
+ # State 3.5: ENABLED, competency count unreadable. Distinguish "we couldn't
187
+ # read the count" from "the count is genuinely zero" — falling into State 3
188
+ # tells the admin "no competencies configured" when the org may actually
189
+ # have several. Route to list-competencies to let the admin see the truth.
190
+ if [[ "$CALL_SCORING_ENABLED" == "true" && -z "$COMPETENCY_COUNT" ]]; then
191
+ echo "Call Coaching is already enabled — competency count could not be read."
192
+ echo " Ready for edit / deactivate / create-new flows."
193
+ echo ""
194
+ echo "Next: $SCRIPT_DIR/list-competencies.sh \"$ORG_ALIAS\" (to inspect the configured set)"
195
+ exit 0
196
+ fi
197
+
198
+ # State 3: ENABLED, no competencies.
199
+ if [[ "$CALL_SCORING_ENABLED" == "true" ]]; then
200
+ echo "Call Coaching is already enabled — no competencies configured yet."
201
+ echo " Ready for OOTB competency install / custom competency creation."
202
+ echo ""
203
+ echo "Next: $SCRIPT_DIR/install-ootb-competencies.sh --list (show the OOTB set, then ask OOTB vs custom)"
204
+ exit 0
205
+ fi
206
+
207
+ # State 2: AVAILABLE, NOT ENABLED — enable path.
208
+
209
+ # 2a-pre. License gate — catch unlicensed orgs BEFORE the "ECI not enabled"
210
+ # refusal below. ConversationalIntelligenceSettings returns HTTP 200 with
211
+ # enableCallCoaching=false on BOTH licensed-ECI-off and genuinely unlicensed
212
+ # orgs (the two are indistinguishable at that read), so State 1a above
213
+ # doesn't catch the unlicensed case. EinsteinGptSettings faulting with
214
+ # TYPE_UNAVAILABLE is the strongest license-gap signal we have without
215
+ # doing a write — Einstein GenAI Platform is a hard prerequisite for AI
216
+ # Call Scoring, so an org that can't even see that settings type won't be
217
+ # able to enable Call Coaching regardless of what the ECI toggle says.
218
+ # W-24026047.
219
+ #
220
+ # Not perfect: an org could have the GenAI platform present but still lack
221
+ # the specific Call Scoring license. State 2c's ECICallScoringAI
222
+ # OrgPermission check catches that residual case after GenAI is enabled.
223
+ if [[ "$GENAI_CLASS" == TYPE_UNAVAILABLE:* ]]; then
224
+ echo "AI call coaching is not available in this org."
225
+ echo ""
226
+ echo " You need Agentforce for Sales to enable AI Call Scoring."
227
+ echo " Without Agentforce for Sales, you can still set up manual call scoring"
228
+ echo " by navigating to Salesforce Go setup in your org."
229
+ exit 1
230
+ fi
231
+
232
+ echo "State: AVAILABLE, NOT ENABLED — proceeding with enable flow."
233
+ echo ""
234
+
235
+ # 2a. License gate — ECICallScoringAI OrgPermission is the authoritative
236
+ # license signal and must be checked BEFORE the "ECI not enabled" refusal
237
+ # below. ConversationalIntelligenceSettings reads enableCallCoaching=false on
238
+ # BOTH a licensed-but-toggled-off org and a genuinely unlicensed one — those
239
+ # are indistinguishable at that read. Checking here, ahead of 2b, prevents a
240
+ # no-license org from being misdiagnosed as "just flip ECI in Setup" (a
241
+ # regression this skill's eval guards against — see the NoLicense dataset).
242
+ ECICALLSCORINGAI_ENABLED=""
243
+ if ECICALLSCORINGAI_RAW=$(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
244
+ --query "SELECT SettingValue FROM OrganizationSettingsDetail WHERE SettingName='ECICallScoringAI' AND DataType='OrgPermissionSettings'" \
245
+ --json 2>/dev/null); then
246
+ ECICALLSCORINGAI_JSON=$(echo "$ECICALLSCORINGAI_RAW" | awk '/^{/,EOF')
247
+ if echo "$ECICALLSCORINGAI_JSON" | jq -e '.status == 0' >/dev/null 2>&1; then
248
+ ECICALLSCORINGAI_ENABLED=$(echo "$ECICALLSCORINGAI_JSON" | jq -r '.result.records[0].SettingValue // "false"')
249
+ fi
250
+ fi
251
+
252
+ if [[ -z "$ECICALLSCORINGAI_ENABLED" ]]; then
253
+ echo "Could not determine AI call coaching license state — ECICallScoringAI query failed."
254
+ echo " Verify the CLI session is valid (sf org login web --alias $ORG_ALIAS) and re-run."
255
+ exit 1
256
+ fi
257
+
258
+ if [[ "$ECICALLSCORINGAI_ENABLED" != "true" ]]; then
259
+ echo "AI call coaching is not available in this org and org requires Agentforce for Sales license."
260
+ exit 1
261
+ fi
262
+
263
+ # 2b. ECI must be enabled — this skill is ECI-only.
264
+ if [[ "$ECI_ENABLED" != "true" ]]; then
265
+ echo "Error: ECI (Einstein Conversation Insights) is not enabled."
266
+ echo " Setup → Salesforce Go → Einstein Conversation Insights → Turn ON, then re-run."
267
+ exit 1
268
+ fi
269
+
270
+ # 2c. Enable Einstein GenAI if off (PRD explicitly authorizes this).
271
+ if [[ "$GENAI_ENABLED" != "true" ]]; then
272
+ echo "Enabling Einstein Generative AI Platform (required for AI scoring)..."
273
+ GENAI_ENABLE_REQ=$(cat <<'EOF'
274
+ <?xml version="1.0" encoding="utf-8"?>
275
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:met="http://soap.sforce.com/2006/04/metadata">
276
+ <soapenv:Header>
277
+ <met:SessionHeader><met:sessionId>{{ACCESS_TOKEN}}</met:sessionId></met:SessionHeader>
278
+ </soapenv:Header>
279
+ <soapenv:Body>
280
+ <met:updateMetadata>
281
+ <met:metadata xsi:type="met:EinsteinGptSettings" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
282
+ <met:fullName>EinsteinGpt</met:fullName>
283
+ <met:enableEinsteinGptPlatform>true</met:enableEinsteinGptPlatform>
284
+ </met:metadata>
285
+ </met:updateMetadata>
286
+ </soapenv:Body>
287
+ </soapenv:Envelope>
288
+ EOF
289
+ )
290
+ GENAI_ENABLE_REQ="${GENAI_ENABLE_REQ//\{\{ACCESS_TOKEN\}\}/$ACCESS_TOKEN}"
291
+ GENAI_RESULT=$(classify_field "$(soap_call "update" "$GENAI_ENABLE_REQ")" "success")
292
+ if [[ "$GENAI_RESULT" == "true" ]]; then
293
+ echo " Einstein GenAI enabled"
294
+ else
295
+ case "$GENAI_RESULT" in
296
+ TYPE_UNAVAILABLE:*|false)
297
+ # Org isn't licensed for EinsteinGptSettings, or the update was
298
+ # explicitly refused with no fault and no <errors> detail — either
299
+ # way, this is a licensing gap, not a transient error, so point at
300
+ # the actual fix instead of telling the admin to just retry.
301
+ echo " Einstein Generative AI Platform cannot be enabled — $(describe_failure "$GENAI_RESULT")."
302
+ echo ""
303
+ echo " You need Agentforce for Sales to enable AI Call Scoring."
304
+ echo " Without Agentforce for Sales, you can still set up manual call scoring"
305
+ echo " by navigating to Salesforce Go setup in your org."
306
+ ;;
307
+ UPDATE_REJECTED:*)
308
+ # Salesforce returned success=false with an <errors> block — surface
309
+ # the actual statusCode/message instead of guessing at a cause.
310
+ echo " Einstein GenAI enable failed: $(describe_failure "$GENAI_RESULT")"
311
+ ;;
312
+ AUTH_ERROR:*)
313
+ echo " Einstein GenAI enable failed: $(describe_failure "$GENAI_RESULT")"
314
+ echo ""
315
+ echo " Verify the CLI session is valid (sf org login web --alias $ORG_ALIAS) and re-run."
316
+ ;;
317
+ *)
318
+ echo " Einstein GenAI enable failed: $(describe_failure "$GENAI_RESULT")"
319
+ echo ""
320
+ echo " Try again in a moment; if it persists, check the org's availability."
321
+ ;;
322
+ esac
323
+ exit 1
324
+ fi
325
+ echo ""
326
+ else
327
+ echo "Einstein Generative AI Platform is already enabled — skipping."
328
+ echo ""
329
+ fi
330
+
331
+ # Enable Call Coaching (enableECICallScoring) — skip the update if it's
332
+ # already on (state detection above only short-circuits when competencies
333
+ # also exist; an org can have enableECICallScoring=true with zero
334
+ # competencies and still reach here if that read raced with an out-of-band
335
+ # change).
336
+ if [[ "$CALL_SCORING_ENABLED" != "true" ]]; then
337
+ echo "Enabling Call Coaching..."
338
+ CS_ENABLE_REQ=$(cat <<'EOF'
339
+ <?xml version="1.0" encoding="utf-8"?>
340
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:met="http://soap.sforce.com/2006/04/metadata">
341
+ <soapenv:Header>
342
+ <met:SessionHeader><met:sessionId>{{ACCESS_TOKEN}}</met:sessionId></met:SessionHeader>
343
+ </soapenv:Header>
344
+ <soapenv:Body>
345
+ <met:updateMetadata>
346
+ <met:metadata xsi:type="met:ConversationalIntelligenceSettings" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
347
+ <met:fullName>ConversationalIntelligence</met:fullName>
348
+ <met:enableECICallScoring>true</met:enableECICallScoring>
349
+ </met:metadata>
350
+ </met:updateMetadata>
351
+ </soapenv:Body>
352
+ </soapenv:Envelope>
353
+ EOF
354
+ )
355
+ CS_ENABLE_REQ="${CS_ENABLE_REQ//\{\{ACCESS_TOKEN\}\}/$ACCESS_TOKEN}"
356
+ CS_RESULT=$(classify_field "$(soap_call "update" "$CS_ENABLE_REQ")" "success")
357
+
358
+ if [[ "$CS_RESULT" == "true" ]]; then
359
+ echo " Call Coaching enabled"
360
+ else
361
+ # ECI enablement is asynchronous — for a short window after the ECI toggle
362
+ # flips on, the dependent enableECICallScoring field is server-side
363
+ # read-only and updateMetadata returns INSUFFICIENT_ACCESS_OR_READONLY.
364
+ # This is a transient state that self-resolves in a few minutes, not a
365
+ # permanent failure — surface a wait-and-retry message instead of the
366
+ # generic error. W-23968462.
367
+ case "$CS_RESULT" in
368
+ WRITE_ERROR:INSUFFICIENT_ACCESS_OR_READONLY*)
369
+ echo " Einstein Conversation Insights is still finishing turning on."
370
+ echo " Wait a few minutes, then re-run this to enable Call Coaching."
371
+ ;;
372
+ *)
373
+ echo " Call Coaching enable failed: $(describe_failure "$CS_RESULT")"
374
+ ;;
375
+ esac
376
+ exit 1
377
+ fi
378
+ else
379
+ echo "Call Coaching is already enabled — skipping."
380
+ fi
381
+
382
+ echo ""
383
+
384
+ # Enable the CallCoachingReady OrgPermission — not a Metadata API type, so
385
+ # it's flipped via Tooling API PATCH on OrganizationSettingsDetail (the same
386
+ # object the OrgPermission reads above use), looked up by Id first.
387
+ CCR_LOOKUP=$(sf api request rest \
388
+ "services/data/v68.0/tooling/query/?q=SELECT+Id,SettingValue+FROM+OrganizationSettingsDetail+WHERE+SettingName='CallCoachingReady'+AND+DataType='OrgPermissionSettings'" \
389
+ --target-org "$ORG_ALIAS" 2>/dev/null) || true
390
+
391
+ CCR_COUNT=$(echo "$CCR_LOOKUP" | jq -r '.totalSize // 0' 2>/dev/null || echo 0)
392
+ if [[ "$CCR_COUNT" == "0" ]]; then
393
+ echo "Could not locate the CallCoachingReady OrgPermission record on this org."
394
+ exit 1
395
+ fi
396
+
397
+ CCR_ID=$(echo "$CCR_LOOKUP" | jq -r '.records[0].Id')
398
+ CCR_CURRENT=$(echo "$CCR_LOOKUP" | jq -r '.records[0].SettingValue')
399
+
400
+ if [[ "$CCR_CURRENT" == "true" ]]; then
401
+ echo "CallCoachingReady is already enabled — skipping."
402
+ else
403
+ echo "Enabling CallCoachingReady..."
404
+ if CCR_PATCH_RESP=$(sf api request rest \
405
+ "services/data/v68.0/tooling/sobjects/OrganizationSettingsDetail/${CCR_ID}" \
406
+ --target-org "$ORG_ALIAS" \
407
+ --method PATCH \
408
+ --header 'Content-Type: application/json' \
409
+ --body '{"SettingValue": true}' \
410
+ 2>/dev/null); then
411
+ echo " CallCoachingReady enabled"
412
+ else
413
+ echo " Failed to enable CallCoachingReady"
414
+ echo " $CCR_PATCH_RESP"
415
+ exit 1
416
+ fi
417
+ fi
418
+
419
+ echo ""
420
+
421
+ # 2d. Verify.
422
+ echo "Verifying..."
423
+ sleep 2
424
+ VERIFY_CLASS=$(classify_field "$(soap_read "ConversationalIntelligenceSettings" "ConversationalIntelligence")" "enableECICallScoring")
425
+
426
+ if [[ "$VERIFY_CLASS" == "true" ]]; then
427
+ echo " Confirmed: Call Coaching is now enabled"
428
+ echo ""
429
+ echo "Call Coaching enabled. Ready for OOTB competency install / custom creation."
430
+ echo ""
431
+ echo "Next: $SCRIPT_DIR/install-ootb-competencies.sh --list (show the OOTB set, then ask OOTB vs custom)"
432
+ exit 0
433
+ else
434
+ echo " Error: Verification failed: $(describe_failure "$VERIFY_CLASS")"
435
+ exit 1
436
+ fi