@salesforce/afv-skills 1.46.0 → 1.48.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 (317) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-observe/SKILL.md +32 -4
  3. package/skills/agentforce-observe/references/ahm-alerts.md +719 -0
  4. package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +275 -0
  5. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/README.md +32 -0
  6. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls +75 -0
  7. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls-meta.xml +5 -0
  8. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/interview-answers.json +13 -0
  9. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +10 -0
  10. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/create.json +20 -0
  11. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/update.json +16 -0
  12. package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +273 -0
  13. package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +236 -0
  14. package/skills/consumer-goods-promotion-bo-api-deploy/references/reference-example-set-comment-value.md +132 -0
  15. package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +211 -0
  16. package/skills/dx-devops-project-manage/SKILL.md +197 -0
  17. package/skills/dx-devops-project-manage/examples/common-workflows.md +197 -0
  18. package/skills/dx-devops-project-manage/references/cli-commands.md +295 -0
  19. package/skills/dx-devops-project-manage/scripts/create-project.sh +48 -0
  20. package/skills/dx-devops-project-manage/scripts/list-projects.sh +51 -0
  21. package/skills/dx-devops-project-manage/scripts/update-project.sh +96 -0
  22. package/skills/education-cloud-academic-calendar-generate/SKILL.md +225 -0
  23. package/skills/education-cloud-academic-calendar-generate/examples/quarter-calendar.json +47 -0
  24. package/skills/education-cloud-academic-calendar-generate/examples/sample-output.md +57 -0
  25. package/skills/education-cloud-academic-calendar-generate/examples/semester-calendar.json +54 -0
  26. package/skills/education-cloud-academic-calendar-generate/references/calendar-systems.md +127 -0
  27. package/skills/education-cloud-academic-calendar-generate/references/date-validation.md +222 -0
  28. package/skills/education-cloud-academic-calendar-generate/references/foundation_prerequisites.md +40 -0
  29. package/skills/education-cloud-academic-calendar-generate/scripts/validate_calendar_dates.py +143 -0
  30. package/skills/education-cloud-course-catalog-migrate/SKILL.md +321 -0
  31. package/skills/education-cloud-course-catalog-migrate/references/gotchas-detail.md +16 -0
  32. package/skills/education-cloud-course-catalog-migrate/references/gotchas.md +16 -0
  33. package/skills/education-cloud-course-catalog-migrate/references/large-catalog-handling.md +42 -0
  34. package/skills/education-cloud-course-catalog-migrate/scripts/batch_courses.py +36 -0
  35. package/skills/education-cloud-course-catalog-migrate/scripts/detect_linked_courses.py +51 -0
  36. package/skills/education-cloud-course-catalog-migrate/scripts/detect_modality_variants.py +48 -0
  37. package/skills/education-cloud-course-catalog-migrate/scripts/resolve_api_version.py +43 -0
  38. package/skills/education-cloud-course-catalog-migrate/scripts/split_course_code.py +39 -0
  39. package/skills/education-cloud-course-catalog-migrate/scripts/validate_completeness.py +54 -0
  40. package/skills/education-cloud-domain-configure/SKILL.md +234 -0
  41. package/skills/education-cloud-domain-configure/references/dependent-features.md +198 -0
  42. package/skills/education-cloud-domain-configure/references/domain-features/academic-operations.md +386 -0
  43. package/skills/education-cloud-domain-configure/references/domain-features/alumni-relations.md +155 -0
  44. package/skills/education-cloud-domain-configure/references/domain-features/cross-domain-dependencies.md +18 -0
  45. package/skills/education-cloud-domain-configure/references/domain-features/industriessettings-toggle-reference.md +43 -0
  46. package/skills/education-cloud-domain-configure/references/domain-features/mentoring.md +198 -0
  47. package/skills/education-cloud-domain-configure/references/domain-features/recruitment-admissions.md +315 -0
  48. package/skills/education-cloud-domain-configure/references/domain-features/student-management.md +155 -0
  49. package/skills/education-cloud-domain-configure/references/domain-features/student-success.md +249 -0
  50. package/skills/education-cloud-domain-configure/references/domain-features.md +27 -0
  51. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +23 -0
  52. package/skills/education-cloud-domain-configure/references/toggle-write-path.md +67 -0
  53. package/skills/education-cloud-domain-configure/scripts/check_license_provisioned.py +62 -0
  54. package/skills/education-cloud-domain-configure/scripts/check_org_edition.py +44 -0
  55. package/skills/education-cloud-domain-configure/scripts/check_permission_assigned.py +39 -0
  56. package/skills/education-cloud-multi-campus-configure/SKILL.md +7 -0
  57. package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +1 -2
  58. package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +177 -0
  59. package/skills/education-cloud-student-recruitment-agent-configure/references/agent-and-subagents.md +151 -0
  60. package/skills/education-cloud-student-recruitment-agent-configure/references/customer-narration.md +34 -0
  61. package/skills/education-cloud-student-recruitment-agent-configure/references/execution-model.md +54 -0
  62. package/skills/education-cloud-student-recruitment-agent-configure/references/flows.md +82 -0
  63. package/skills/education-cloud-student-recruitment-agent-configure/references/grounding.md +199 -0
  64. package/skills/education-cloud-student-recruitment-agent-configure/references/permissions.md +183 -0
  65. package/skills/education-cloud-student-recruitment-agent-configure/references/platform-enablement.md +82 -0
  66. package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +158 -0
  67. package/skills/education-cloud-student-recruitment-agent-configure/references/routing.md +141 -0
  68. package/skills/experience-cms-brand-apply/SKILL.md +5 -5
  69. package/skills/experience-cms-brand-create/SKILL.md +2 -2
  70. package/skills/experience-cms-content-generate/SKILL.md +1 -0
  71. package/skills/experience-cms-content-render/SKILL.md +173 -0
  72. package/skills/experience-cms-content-render/assets/angular/DetailPage.component.ts +25 -0
  73. package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +133 -0
  74. package/skills/experience-cms-content-render/assets/angular/TypeList.component.ts +38 -0
  75. package/skills/experience-cms-content-render/assets/angular/TypeRenderer.component.ts +90 -0
  76. package/skills/experience-cms-content-render/assets/angular/cms-content.component.ts +248 -0
  77. package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +100 -0
  78. package/skills/experience-cms-content-render/assets/react/DetailPage.tsx +20 -0
  79. package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +129 -0
  80. package/skills/experience-cms-content-render/assets/react/TypeList.tsx +40 -0
  81. package/skills/experience-cms-content-render/assets/react/TypeRenderer.tsx +64 -0
  82. package/skills/experience-cms-content-render/assets/react/heuristicRenderer.tsx +310 -0
  83. package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +129 -0
  84. package/skills/experience-cms-content-render/assets/shared/cmsContentType.ts +49 -0
  85. package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +96 -0
  86. package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +55 -0
  87. package/skills/experience-cms-content-render/references/bulk-loading.md +60 -0
  88. package/skills/experience-cms-content-render/references/codegen-guardrails.md +111 -0
  89. package/skills/experience-cms-content-render/references/detail-pages.md +87 -0
  90. package/skills/experience-cms-content-render/references/embed-recipes.md +127 -0
  91. package/skills/experience-cms-content-render/references/failure-modes.md +96 -0
  92. package/skills/experience-cms-content-render/references/heuristic-render-rules.md +131 -0
  93. package/skills/experience-cms-content-render/references/init-scaffold.md +122 -0
  94. package/skills/experience-cms-content-render/references/interaction-model.md +173 -0
  95. package/skills/experience-cms-content-render/references/package-api.md +106 -0
  96. package/skills/experience-cms-content-render/references/schema-sync.md +114 -0
  97. package/skills/experience-cms-content-render/references/styling-scopes.md +65 -0
  98. package/skills/experience-cms-content-render/references/verify.md +49 -0
  99. package/skills/experience-cms-content-type-generate/SKILL.md +2 -2
  100. package/skills/experience-content-media-stock-image-search/SKILL.md +5 -4
  101. package/skills/experience-search-coordinate/SKILL.md +198 -0
  102. package/skills/experience-search-coordinate/assets/search-payload-template.json +25 -0
  103. package/skills/experience-search-coordinate/references/content-route.md +313 -0
  104. package/skills/experience-search-coordinate/references/content-type-discovery.md +57 -0
  105. package/skills/experience-search-coordinate/references/media-route.md +172 -0
  106. package/skills/experience-search-coordinate/references/scope-resolution.md +14 -0
  107. package/skills/experience-ui-bundle-localize/SKILL.md +1 -1
  108. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +5 -3
  109. package/skills/experience-ui-bundle-project-generate/SKILL.md +18 -14
  110. package/skills/experience-ui-bundle-project-generate/references/angular-project-generate.md +22 -0
  111. package/skills/experience-ui-bundle-project-generate/references/react-project-generate.md +20 -0
  112. package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +58 -54
  113. package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +6 -0
  114. package/skills/experience-ui-bundle-salesforce-data-access/references/graphiti-cli.md +2 -2
  115. package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +6 -0
  116. package/skills/experience-ui-bundle-salesforce-data-access/references/rest-and-integration.md +2 -1
  117. package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +6 -0
  118. package/skills/experience-ui-bundle-site-generate/SKILL.md +59 -8
  119. package/skills/experience-ui-bundle-site-generate/references/configure-metadata-digital-experience.md +8 -3
  120. package/skills/experience-ui-bundle-site-generate/references/configure-metadata-language-settings.md +120 -0
  121. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +336 -0
  122. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +143 -0
  123. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +127 -0
  124. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-deploy-commands.md +116 -0
  125. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +111 -0
  126. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-overview.md +312 -0
  127. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +171 -0
  128. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-state-tracking.md +64 -0
  129. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-trigger-handlers.md +122 -0
  130. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-overview.md +335 -0
  131. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-user-provisioning-details.md +140 -0
  132. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +196 -0
  133. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-metadata-cache-generation.md +155 -0
  134. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +307 -0
  135. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +211 -0
  136. package/skills/life-sciences-fieldsalesrep-coordinate/references/state-machine-and-changes.md +108 -0
  137. package/skills/life-sciences-kam-coordinate/SKILL.md +241 -0
  138. package/skills/life-sciences-kam-coordinate/references/orchestration-flow.md +152 -0
  139. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +79 -0
  140. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-deploy-commands.md +131 -0
  141. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-kam-config-records.md +85 -0
  142. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +112 -0
  143. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-overview.md +202 -0
  144. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +67 -0
  145. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-state-tracking.md +65 -0
  146. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-trigger-handlers.md +123 -0
  147. package/skills/life-sciences-kam-coordinate/references/stage-4-participant-role-and-sprint.md +89 -0
  148. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +337 -0
  149. package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +248 -0
  150. package/skills/life-sciences-kam-coordinate/references/stage-6-ipad-validation-script.md +35 -0
  151. package/skills/life-sciences-kam-coordinate/references/stage-6-metadata-cache-generation.md +155 -0
  152. package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-details.md +146 -0
  153. package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-overview.md +89 -0
  154. package/skills/life-sciences-kam-coordinate/references/state-machine-and-changes.md +114 -0
  155. package/skills/life-sciences-prerequisites-validate/SKILL.md +138 -0
  156. package/skills/life-sciences-prerequisites-validate/references/checks-org-settings.md +190 -0
  157. package/skills/life-sciences-prerequisites-validate/references/checks-user-and-package.md +211 -0
  158. package/skills/life-sciences-territory-configure/SKILL.md +217 -0
  159. package/skills/life-sciences-territory-configure/references/territory-metadata.md +262 -0
  160. package/skills/platform-dsar-policy-manage/SKILL.md +272 -0
  161. package/skills/platform-dsar-policy-manage/references/configure.md +106 -0
  162. package/skills/platform-dsar-policy-manage/references/export-and-history.md +123 -0
  163. package/skills/platform-dsar-policy-manage/references/gap-analysis-guide.md +150 -0
  164. package/skills/platform-dsar-policy-manage/references/gap-scan.md +129 -0
  165. package/skills/platform-dsar-policy-manage/references/headless-sor.md +59 -0
  166. package/skills/platform-dsar-policy-manage/references/report-format.md +59 -0
  167. package/skills/platform-dsar-policy-manage/scripts/tests/__init__.py +0 -0
  168. package/skills/platform-dsar-policy-manage/scripts/tests/test_validate_policy_tree.py +76 -0
  169. package/skills/platform-dsar-policy-manage/scripts/validate-policy-tree.py +130 -0
  170. package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +359 -0
  171. package/skills/platform-salesforce-connect-adapter-generate/references/official-examples.md +69 -0
  172. package/skills/platform-salesforce-connect-adapter-generate/references/scenarios.md +187 -0
  173. package/skills/service-agentforce-channel-configure/SKILL.md +33 -28
  174. package/skills/service-agentforce-human-escalation-configure/SKILL.md +205 -0
  175. package/skills/service-agentforce-human-escalation-configure/assets/escalation-thresholds.instructions.md +37 -0
  176. package/skills/service-agentforce-human-escalation-configure/assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml +173 -0
  177. package/skills/service-agentforce-human-escalation-configure/assets/package.xml +8 -0
  178. package/skills/service-agentforce-human-escalation-configure/assets/sfdx-project.json +5 -0
  179. package/skills/service-agentforce-human-escalation-configure/references/classifier-contracts.md +103 -0
  180. package/skills/service-agentforce-human-escalation-configure/references/escalation-mechanism.md +63 -0
  181. package/skills/service-agentforce-human-escalation-configure/references/failure-policy.md +44 -0
  182. package/skills/service-agentforce-human-escalation-configure/references/nga-escalation.md +57 -0
  183. package/skills/service-agentforce-human-escalation-configure/references/runtime-verification.md +72 -0
  184. package/skills/service-agentforce-human-escalation-configure/scripts/classify-agent-active.mjs +49 -0
  185. package/skills/service-agentforce-human-escalation-configure/scripts/classify-nga-escalation.mjs +166 -0
  186. package/skills/service-agentforce-human-escalation-configure/scripts/extract-outbound-route.mjs +67 -0
  187. package/skills/service-agentforce-human-escalation-configure/scripts/patch-escalation-surfaces.mjs +131 -0
  188. package/skills/service-agentforce-human-escalation-configure/scripts/tests/__init__.py +0 -0
  189. package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +249 -0
  190. package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +899 -0
  191. package/skills/service-agentforce-human-escalation-configure/scripts/verify-and-configure.sh +705 -0
  192. package/skills/service-agentforce-human-escalation-configure/scripts/verify-escalation-config.mjs +89 -0
  193. package/skills/service-de-channel-routing-configure/SKILL.md +13 -5
  194. package/skills/service-de-channel-routing-configure/scripts/tests/test_skill_contract.py +25 -0
  195. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +23 -10
  196. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -48
  197. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +6 -2
  198. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +20 -27
  199. package/skills/service-native-voice-recording-transcription-configure/SKILL.md +47 -27
  200. package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +13 -9
  201. package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +104 -45
  202. package/skills/service-omni-agent-users-create/SKILL.md +96 -0
  203. package/skills/service-omni-agent-users-create/assets/create-users.apex.template +80 -0
  204. package/skills/service-omni-agent-users-create/references/apex-patterns.md +135 -0
  205. package/skills/service-omni-agent-users-create/references/apex-template-notes.md +40 -0
  206. package/skills/service-omni-agent-users-create/scripts/detect-and-create.sh +206 -0
  207. package/skills/service-omni-agent-users-create/scripts/detect-existing.sh +122 -0
  208. package/skills/service-omni-agent-users-create/scripts/run-create.sh +298 -0
  209. package/skills/service-omni-agent-users-create/scripts/tests/test_user_create_security.py +171 -0
  210. package/skills/service-omni-agent-work-sharing-configure/SKILL.md +78 -0
  211. package/skills/service-omni-agent-work-sharing-configure/references/api-notes.md +31 -0
  212. package/skills/service-omni-agent-work-sharing-configure/scripts/configure-and-report.sh +186 -0
  213. package/skills/service-omni-agent-work-sharing-configure/scripts/tests/_bootstrap.py +120 -0
  214. package/skills/service-omni-agent-work-sharing-configure/scripts/tests/test_agent_work_sharing_contracts.py +93 -0
  215. package/skills/service-omni-base-settings-configure/SKILL.md +77 -0
  216. package/skills/service-omni-base-settings-configure/assets/force-app/main/default/settings/OmniChannel.settings-meta.xml +8 -0
  217. package/skills/service-omni-base-settings-configure/assets/package.xml +8 -0
  218. package/skills/service-omni-base-settings-configure/assets/sfdx-project.json +12 -0
  219. package/skills/service-omni-base-settings-configure/references/api-notes.md +121 -0
  220. package/skills/service-omni-base-settings-configure/scripts/configure-and-report.sh +164 -0
  221. package/skills/service-omni-base-settings-configure/scripts/detect-existing.sh +69 -0
  222. package/skills/service-omni-channel-setup-coordinate/SKILL.md +168 -0
  223. package/skills/service-omni-channel-setup-coordinate/examples/output-templates.md +161 -0
  224. package/skills/service-omni-channel-setup-coordinate/references/gap-catalog.md +99 -0
  225. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +1542 -0
  226. package/skills/service-omni-channel-setup-coordinate/scripts/tests/__init__.py +0 -0
  227. package/skills/service-omni-channel-setup-coordinate/scripts/tests/_bootstrap.py +223 -0
  228. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +804 -0
  229. package/skills/service-omni-command-center-analyze/SKILL.md +84 -0
  230. package/skills/service-omni-command-center-analyze/references/api-notes.md +54 -0
  231. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +198 -0
  232. package/skills/service-omni-command-center-analyze/scripts/tests/_bootstrap.py +115 -0
  233. package/skills/service-omni-command-center-analyze/scripts/tests/test_command_center_analyze_contracts.py +165 -0
  234. package/skills/service-omni-permission-set-assign/SKILL.md +88 -0
  235. package/skills/service-omni-permission-set-assign/assets/package.xml +8 -0
  236. package/skills/service-omni-permission-set-assign/references/api-notes.md +143 -0
  237. package/skills/service-omni-permission-set-assign/scripts/verify-and-assign.sh +397 -0
  238. package/skills/service-omni-presence-status-deploy/SKILL.md +87 -0
  239. package/skills/service-omni-presence-status-deploy/assets/package.xml +12 -0
  240. package/skills/service-omni-presence-status-deploy/references/api-notes.md +115 -0
  241. package/skills/service-omni-presence-status-deploy/scripts/deploy-and-report.sh +422 -0
  242. package/skills/service-omni-presence-user-config-deploy/SKILL.md +80 -0
  243. package/skills/service-omni-presence-user-config-deploy/references/api-notes.md +60 -0
  244. package/skills/service-omni-presence-user-config-deploy/scripts/deploy-and-report.sh +261 -0
  245. package/skills/service-omni-presence-user-config-deploy/scripts/tests/_bootstrap.py +106 -0
  246. package/skills/service-omni-presence-user-config-deploy/scripts/tests/test_presence_user_config_contracts.py +142 -0
  247. package/skills/service-omni-queue-deploy/SKILL.md +91 -0
  248. package/skills/service-omni-queue-deploy/references/api-notes.md +140 -0
  249. package/skills/service-omni-queue-deploy/scripts/verify-and-align.sh +470 -0
  250. package/skills/service-omni-queue-members-assign/SKILL.md +93 -0
  251. package/skills/service-omni-queue-members-assign/references/api-notes.md +144 -0
  252. package/skills/service-omni-queue-members-assign/scripts/tests/_bootstrap.py +67 -0
  253. package/skills/service-omni-queue-members-assign/scripts/tests/test_queue_members_contracts.py +73 -0
  254. package/skills/service-omni-queue-members-assign/scripts/verify-and-bind.sh +362 -0
  255. package/skills/service-omni-queue-routing-config-deploy/SKILL.md +101 -0
  256. package/skills/service-omni-queue-routing-config-deploy/references/api-notes.md +127 -0
  257. package/skills/service-omni-queue-routing-config-deploy/scripts/tests/_bootstrap.py +90 -0
  258. package/skills/service-omni-queue-routing-config-deploy/scripts/tests/test_queue_routing_config_contracts.py +70 -0
  259. package/skills/service-omni-queue-routing-config-deploy/scripts/upsert-and-report.sh +538 -0
  260. package/skills/service-omni-routing-flow-deploy/SKILL.md +111 -0
  261. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.SkillsBased.flow-meta.xml +94 -0
  262. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.flow-meta.xml +88 -0
  263. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Cases.flow-meta.xml +144 -0
  264. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.SkillsBased.flow-meta.xml +94 -0
  265. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.flow-meta.xml +88 -0
  266. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCalls.flow-meta.xml +144 -0
  267. package/skills/service-omni-routing-flow-deploy/assets/package.xml +11 -0
  268. package/skills/service-omni-routing-flow-deploy/assets/sfdx-project.json +11 -0
  269. package/skills/service-omni-routing-flow-deploy/references/api-notes.md +265 -0
  270. package/skills/service-omni-routing-flow-deploy/scripts/deploy-and-report.sh +684 -0
  271. package/skills/service-omni-service-channel-configure/SKILL.md +83 -0
  272. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Cases.serviceChannel-meta.xml +9 -0
  273. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Incidents.serviceChannel-meta.xml +9 -0
  274. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/MessagingSessions.serviceChannel-meta.xml +9 -0
  275. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/VoiceCalls.serviceChannel-meta.xml +9 -0
  276. package/skills/service-omni-service-channel-configure/assets/package.xml +11 -0
  277. package/skills/service-omni-service-channel-configure/assets/sfdx-project.json +12 -0
  278. package/skills/service-omni-service-channel-configure/references/api-notes.md +84 -0
  279. package/skills/service-omni-service-channel-configure/scripts/deploy-and-report.sh +383 -0
  280. package/skills/service-omni-sidebar-configure/SKILL.md +77 -0
  281. package/skills/service-omni-sidebar-configure/references/api-notes.md +51 -0
  282. package/skills/service-omni-sidebar-configure/scripts/enable-and-report.sh +315 -0
  283. package/skills/service-omni-sidebar-configure/scripts/tests/_bootstrap.py +142 -0
  284. package/skills/service-omni-sidebar-configure/scripts/tests/test_sidebar_contracts.py +139 -0
  285. package/skills/service-omni-skills-based-routing-configure/SKILL.md +81 -0
  286. package/skills/service-omni-skills-based-routing-configure/references/api-notes.md +50 -0
  287. package/skills/service-omni-skills-based-routing-configure/scripts/configure-and-report.sh +270 -0
  288. package/skills/service-omni-skills-based-routing-configure/scripts/tests/_bootstrap.py +120 -0
  289. package/skills/service-omni-skills-based-routing-configure/scripts/tests/test_sbr_contracts.py +129 -0
  290. package/skills/service-omni-supervisor-config-deploy/SKILL.md +97 -0
  291. package/skills/service-omni-supervisor-config-deploy/assets/force-app/main/default/omniSupervisorConfigs/Omni_Supervisor.omniSupervisorConfig-meta.xml +9 -0
  292. package/skills/service-omni-supervisor-config-deploy/assets/package.xml +8 -0
  293. package/skills/service-omni-supervisor-config-deploy/assets/sfdx-project.json +12 -0
  294. package/skills/service-omni-supervisor-config-deploy/references/api-notes.md +205 -0
  295. package/skills/service-omni-supervisor-config-deploy/scripts/deploy-and-report.sh +586 -0
  296. package/skills/service-omni-supervisor-config-deploy/scripts/tests/_bootstrap.py +153 -0
  297. package/skills/service-omni-supervisor-config-deploy/scripts/tests/test_supervisor_config_contracts.py +116 -0
  298. package/skills/service-omni-supervisor-permset-assign/SKILL.md +88 -0
  299. package/skills/service-omni-supervisor-permset-assign/references/api-notes.md +48 -0
  300. package/skills/service-omni-supervisor-permset-assign/scripts/verify-and-assign.sh +316 -0
  301. package/skills/service-omni-supervisor-surface-deploy/SKILL.md +85 -0
  302. package/skills/service-omni-supervisor-surface-deploy/references/api-notes.md +47 -0
  303. package/skills/service-omni-supervisor-surface-deploy/scripts/deploy-and-report.sh +216 -0
  304. package/skills/service-omni-supervisor-surface-deploy/scripts/tests/_bootstrap.py +95 -0
  305. package/skills/service-omni-supervisor-surface-deploy/scripts/tests/test_supervisor_surface_contracts.py +154 -0
  306. package/skills/service-omni-supervisor-users-create/SKILL.md +92 -0
  307. package/skills/service-omni-supervisor-users-create/assets/create-supervisors.apex.template +72 -0
  308. package/skills/service-omni-supervisor-users-create/references/apex-patterns.md +73 -0
  309. package/skills/service-omni-supervisor-users-create/references/apex-template-notes.md +41 -0
  310. package/skills/service-omni-supervisor-users-create/scripts/detect-and-create.sh +197 -0
  311. package/skills/service-omni-supervisor-users-create/scripts/detect-existing.sh +112 -0
  312. package/skills/service-omni-supervisor-users-create/scripts/run-create.sh +296 -0
  313. package/skills/service-omni-work-skill-routing-configure/SKILL.md +80 -0
  314. package/skills/service-omni-work-skill-routing-configure/references/api-notes.md +67 -0
  315. package/skills/service-omni-work-skill-routing-configure/scripts/configure-and-report.sh +262 -0
  316. package/skills/service-omni-work-skill-routing-configure/scripts/tests/_bootstrap.py +121 -0
  317. package/skills/service-omni-work-skill-routing-configure/scripts/tests/test_work_skill_routing_contracts.py +161 -0
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: service-omni-presence-status-deploy
3
+ description: "Create the standard Available and Busy presence statuses needed for Omni-Channel routing. TRIGGER when users ask to deploy Omni presence statuses, configure agent availability, deploy Omni status metadata, create an Available status for Case, Incident, Messaging, or Voice, or add the standard Busy status. DO NOT TRIGGER for custom status designs, unsupported objects, or PresenceUserConfig capacity."
4
+ allowed-tools: Bash Read Write Edit Glob Grep
5
+ metadata:
6
+ version: "1.0"
7
+ domains: ["Service"]
8
+ minApiVersion: "66.0"
9
+ relatedSkills:
10
+ - "service-omni-base-settings-configure"
11
+ - "service-omni-channel-setup-coordinate"
12
+ - "service-omni-permission-set-assign"
13
+ - "service-omni-service-channel-configure"
14
+ accessCheck:
15
+ - type: license
16
+ value: ServiceCloud
17
+ cliTools:
18
+ - tool: ["jq"]
19
+ semver: ">=1.6"
20
+ - tool: ["sf"]
21
+ semver: ">=2.139.6"
22
+ ---
23
+
24
+ # service-omni-presence-status-deploy
25
+
26
+ Deploy the sObject-specific `Available_<X>` ServicePresenceStatus bound to that sObject's ServiceChannel, and ensure a universal `Busy` status exists, in one atomic Metadata API deploy. Every Omni agent needs an "available for work" status carrying a channel (so incoming work routes) and an "off-work" status (so they can step away). A presence status binds to exactly one ServiceChannel by DeveloperName, so each routing target needs its own Available status. It pairs with `service-omni-permission-set-assign`, which grants agents access to the statuses this skill deploys, and is invoked once per routing target by `service-omni-channel-setup-coordinate`.
27
+
28
+ | Target sObject | Available status | Bound channel |
29
+ |---|---|---|
30
+ | `Case` | `Available_Case` | `Cases` |
31
+ | `Incident` | `Available_Incident` | `Incidents` |
32
+ | `MessagingSession` | `Available_Messaging` | `sfdc_livemessage` |
33
+ | `VoiceCall` | `Available_Voice` | `sfdc_phone` |
34
+
35
+ **Busy is UI-defined, not metadata-defined.** The Omni "Busy vs Online" option is not expressible via the Metadata API — a `ServicePresenceStatus` carries only `<label>` and `<channels>`, and a status with no channels is treated as busy. The conventional busy DeveloperName is simply `Busy`, and most orgs already have one, so this skill reuses an existing `Busy` and deploys the bundled asset only when none exists. A busy status is not required to receive work — the Available status carrying the channel is what routes it.
36
+
37
+ ## Inputs
38
+
39
+ ```bash
40
+ bash scripts/deploy-and-report.sh <org-alias> [Case|Incident|MessagingSession|VoiceCall]
41
+ ```
42
+
43
+ - `org-alias` (required).
44
+ - `sobject_type` (optional, default `Case`).
45
+
46
+ ## Preconditions and safety
47
+
48
+ - Target org authenticated via `sf` CLI (My Domain URL), Service Cloud license, `sf` CLI ≥ 2.139.6.
49
+ - Omni-Channel base settings enabled (`service-omni-base-settings-configure`) — otherwise the deploy fails with `INVALID_TYPE`.
50
+ - The target sObject's ServiceChannel exists (`service-omni-service-channel-configure`) — otherwise the deploy fails with `Value '<channel>' is not valid`. `Incident` requires the Incident Management feature enabled; `MessagingSession` requires Enhanced Messaging.
51
+ - The three-way `safe_to_write` production guard applies.
52
+
53
+ ## Run
54
+
55
+ `deploy-and-report.sh` deploys the `Available_<X>` status (and `Busy` only when the org has none) in one Metadata API call, then maps the per-component `files[].state` to a skill status:
56
+
57
+ - `reused` — every component `Unchanged`.
58
+ - `updated` — any component `Changed`, none `Created`.
59
+ - `created` — any component `Created`.
60
+
61
+ There is no separate detect step — Salesforce's `files[].state` is authoritative. The deploy uses explicit `--metadata` flags (never `--source-dir`) so unrelated assets cannot piggyback.
62
+
63
+ ## Behavior
64
+
65
+ **Reuse over redeploy.** An existing `Busy` is detected and left untouched; only a missing one is deployed. The skill never deletes or overwrites presence statuses it did not deploy — orgs often carry other custom statuses (`Omni_OnBreak`, `availableForMiaw`).
66
+
67
+ **Schema.** The XML omits `<statusType>` (removed in v66; online/busy is inferred from the presence of `<channels>`), and the skill never SOQL-queries `StatusType` (not queryable on v66 — only Id, DeveloperName, MasterLabel are reliable). `Available_Messaging` binds the standard `sfdc_livemessage` channel that Enhanced Messaging always ships.
68
+
69
+ ## Output contract
70
+
71
+ A single JSON object with `status` ∈ `created` | `reused` | `updated` | `blocked`, `sobject_type`, a `presence_statuses` array (each with `developer_name`, `label`, `channels`, `state`, and — for `Busy` — `reused_existing`), `deploy_id`, `manual_actions`, and `blocking_issue`.
72
+
73
+ - `blocked` — deploy failed (missing prereq, schema mismatch, or org error); `manual_actions` names the prerequisite skill to run and `blocking_issue` is an operator-friendly translation of the Salesforce error.
74
+ - `deploy_id` is populated on every run (Salesforce issues one even for no-op deploys).
75
+
76
+ ## Limitations
77
+
78
+ - Deploys only the `Available_<X>` + `Busy` pair — custom statuses require forking the `assets/servicePresenceStatuses/*.xml`.
79
+ - Only `Case`, `Incident`, `MessagingSession`, `VoiceCall` — extend by adding a dispatcher branch and a matching asset.
80
+ - Does not configure `PresenceUserConfig` capacity or decline reasons, and never deletes existing statuses.
81
+
82
+ ## References
83
+
84
+ | File | When to read |
85
+ |---|---|
86
+ | `assets/package.xml` | Load when preparing the explicit Metadata API deployment for the selected Available status and the optional Busy status |
87
+ | `references/api-notes.md` | On unexpected deploy behavior — the v66 XML schema (no `statusType`), channel-binding rules, and MessagingSession-specific notes |
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Package xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <types>
4
+ <members>Available_Case</members>
5
+ <members>Available_Incident</members>
6
+ <members>Available_Messaging</members>
7
+ <members>Available_Voice</members>
8
+ <members>Busy</members>
9
+ <name>ServicePresenceStatus</name>
10
+ </types>
11
+ <version>66.0</version>
12
+ </Package>
@@ -0,0 +1,115 @@
1
+ # API notes — ServicePresenceStatus (v66)
2
+
3
+ ## Schema
4
+
5
+ `ServicePresenceStatus` is a Metadata-only sObject. It has an sObject shell for SOQL discovery, but the interesting state (channels, decline-reason references) lives only in the XML metadata.
6
+
7
+ **v66 XML skeleton:**
8
+
9
+ ```xml
10
+ <?xml version="1.0" encoding="UTF-8"?>
11
+ <ServicePresenceStatus xmlns="http://soap.sforce.com/2006/04/metadata">
12
+ <label>Available for Cases</label>
13
+ <channels>
14
+ <channel>Cases</channel>
15
+ </channels>
16
+ </ServicePresenceStatus>
17
+ ```text
18
+
19
+ **Two elements only:**
20
+
21
+ | Element | Required | Type | Notes |
22
+ |---|---|---|---|
23
+ | `<label>` | yes | string ≤ 80 chars | Display label. On v66 this is `<label>` (older API versions used `<masterLabel>`). |
24
+ | `<channels>` | no | 0..N of `<channel>` | If present, agent is online for those channels. If absent, agent is busy/away (no work received). Each `<channel>` references a `ServiceChannel.DeveloperName`. |
25
+
26
+ **Fields not present in v66 XML:**
27
+
28
+ - `<statusType>` — on v66 this is inferred from the presence of `<channels>`; adding `<statusType>` to the deploy XML causes `Element {statusType} not defined`.
29
+ - `<masterLabel>` — renamed to `<label>`.
30
+
31
+ ## The StatusType concept is not settable or queryable on v66
32
+
33
+ The `StatusType` concept still exists in the Salesforce runtime (query it via `PresenceUserConfigProfile` joins or the presence-status-selector UI) but is not:
34
+
35
+ - Directly settable in metadata XML (v66) — inferred from `<channels>`
36
+ - Directly queryable in SOQL (see below)
37
+ - Directly readable via `sf project retrieve start` — the XML you retrieve is the canonical representation
38
+
39
+ **Runtime implication:** to give an agent a "Busy" status, deploy a `ServicePresenceStatus` with no `<channels>` element. To give an "Online for Cases" status, deploy one with `<channels><channel>Cases</channel></channels>`. Multiple busy statuses are allowed (e.g. `Omni_OnBreak`, `Busy` — both no channels, distinguishable only by label).
40
+
41
+ **Caveat — busy/online status option is UI-only:** the Omni-Channel "Status Options: Online / Busy" toggle is NOT part of the `ServicePresenceStatus` metadata type or a queryable field (confirmed via community + developer.salesforce.com — the type carries only `label` + `channels`). A no-channels status is treated as busy at runtime, but you cannot *set* the toggle headlessly. Because of this, our skill does not mint a custom `Omni_Busy`; it reuses the conventional `Busy` status if it exists (`SELECT Id FROM ServicePresenceStatus WHERE DeveloperName='Busy'`) and only deploys a bundled `Busy` when the org has none — never redeploying (and thus never risking) an existing, correctly-toggled `Busy`.
42
+
43
+ ## StatusType is not SOQL-queryable on v66
44
+
45
+ `ServicePresenceStatus.StatusType` is not SOQL-queryable on v66:
46
+
47
+ ```
48
+ SELECT Id, DeveloperName, StatusType FROM ServicePresenceStatus
49
+ ^
50
+ ERROR at Row:1:Column:40
51
+ No such column 'StatusType' on entity 'ServicePresenceStatus'
52
+ ```text
53
+
54
+ **Queryable fields:** `Id`, `DeveloperName`, `MasterLabel`.
55
+
56
+ **Fields that describe as existing on the sObject but fail in SOQL:** `StatusType`, `Channels` (relationship), various audit fields.
57
+
58
+ **Implication for the audit-log skill:** to enumerate the set of presence statuses on an org, use `SELECT Id, DeveloperName, MasterLabel FROM ServicePresenceStatus`, then optionally `sf project retrieve start` with a `ServicePresenceStatus:*` package to get the per-status XML for channel details.
59
+
60
+ ## Deploy semantics
61
+
62
+ Metadata API deploy of `ServicePresenceStatus:Available_Case` returns per-file state:
63
+
64
+ - `Created` — new status record
65
+ - `Changed` — status existed, XML differed, record updated
66
+ - `Unchanged` — status existed, XML matched (byte-for-byte, after normalization)
67
+
68
+ For our skill's `Available_Case` XML:
69
+
70
+ ```xml
71
+ <ServicePresenceStatus xmlns="http://soap.sforce.com/2006/04/metadata">
72
+ <channels>
73
+ <channel>Cases</channel>
74
+ </channels>
75
+ <label>Available for Cases</label>
76
+ </ServicePresenceStatus>
77
+ ```
78
+
79
+ If the org has an existing `Available_Case` with a different label OR different channels OR extra child elements, deploy will report `Changed` and overwrite. This is intentional for our skill's "canonicalize state" semantics.
80
+
81
+ **XML element ordering does NOT affect Unchanged detection** — Salesforce normalizes before comparing. `<channels>` before `<label>` or after produces the same hash.
82
+
83
+ **Whitespace/indentation does NOT affect Unchanged detection** — Salesforce normalizes whitespace before comparing.
84
+
85
+ ## Common deploy failures + translations
86
+
87
+ | Salesforce error | Skill translation | Prerequisite skill |
88
+ |---|---|---|
89
+ | `INVALID_TYPE: ServicePresenceStatus` | "OmniChannelSettings not enabled — run service-omni-base-settings-configure first" | `service-omni-base-settings-configure` |
90
+ | `INVALID_TYPE: OmniChannel` | Same as above | Same |
91
+ | `INVALID_FIELD_VALUE: 'Cases' is not valid` on `<channel>` reference | "Cases ServiceChannel does not exist — run service-omni-service-channel-configure first" | `service-omni-service-channel-configure` |
92
+ | `Element {statusType} not defined` | "Deploy XML contains `<statusType>` element — remove it; v66 infers status from `<channels>`" | (fix XML) |
93
+ | `INSUFFICIENT_ACCESS_OR_READONLY` | "Executing user lacks Metadata API permissions — re-authenticate as System Administrator" | (re-auth) |
94
+ | `UNKNOWN_EXCEPTION` (transient) | Retry once; if persists, escalate to Salesforce support | (retry) |
95
+
96
+ ## Retrieve semantics
97
+
98
+ `sf project retrieve start --metadata "ServicePresenceStatus:*"` returns one XML file per status but can take several minutes for a handful of statuses (network + Salesforce packaging overhead), which is why this skill does not retrieve — it trusts `sf project deploy start`'s per-component `state` signal instead.
99
+
100
+ ## Latency resilience (deploy async+poll; verify bounded-retry)
101
+
102
+ `deploy-and-report.sh` deploys **async** (`sf project deploy start --async`) then polls `sf project deploy report --job-id <id>` to a terminal state (~20 min budget = 80 × 15s), mirroring the sibling supervisor-config-deploy skill. A synchronous watched deploy raises `ClientTimeoutError` under org load and exits non-zero even when the server-side deploy Succeeds — a false red.
103
+
104
+ The post-deploy binding **verification** retrieve uses **bounded retry with backoff** (5 attempts, 10→40s increasing sleeps) so a single latency-induced *empty* retrieve does not immediately flip an otherwise-successful deploy to blocked. A retrieve that returns XML is authoritative and stops the loop at once (channel present → verified; channel absent → definitively unbound → blocked). The verify still fails closed: after the retry budget is exhausted with no XML, `BINDING_VERIFIED` stays `unverified` and the skill blocks — retries convert transient emptiness into a definitive read, they never assume success.
105
+
106
+ ## Standard vs custom statuses
107
+
108
+ Salesforce ships several standard presence statuses out of the box on orgs with OmniChannel enabled:
109
+
110
+ - `availableForMessaging` (channels: sfdc_livemessage)
111
+ - `availableForMiaw` (channels: sfdc_livemessage)
112
+
113
+ These are shipped by Salesforce and always exist. Our skill's `Available_Case` is a custom addition — deploying it is idempotent (won't collide with the standard ones) but it does occupy the org's DeveloperName namespace. For the busy status we reuse the conventional `Busy` rather than adding a custom one.
114
+
115
+ **Naming convention rule:** never use a DeveloperName that looks like a standard status (e.g. `available*`, `busy*` without a prefix). Our skill uses `Available_Case` (with underscore + object suffix) and `Omni_*` (with `Omni_` prefix) to stay clear of the standard namespace.
@@ -0,0 +1,422 @@
1
+ #!/usr/bin/env bash
2
+ # deploy-and-report.sh - deploy the sObject-specific "Available_<X>" presence status bound to the resolved ServiceChannel and reuse-or-deploy a universal "Busy" status (Busy is UI-only in the Metadata API). Args, channel binding, exit codes: SKILL.md.
3
+
4
+ set -euo pipefail
5
+
6
+ if [ $# -lt 1 ]; then
7
+ echo '{"error":"Missing required argument: org-alias","usage":"bash deploy-and-report.sh <org-alias> [Case|Incident|MessagingSession|VoiceCall]"}' >&2
8
+ exit 1
9
+ fi
10
+
11
+ ORG="$1"
12
+ SOBJECT_TYPE="${2:-Case}"
13
+ CHANNEL_DN_ARG="${3:-}"
14
+
15
+ case "$SOBJECT_TYPE" in
16
+ Case)
17
+ AVAILABLE_STATUS_DN="Available_Case"
18
+ AVAILABLE_STATUS_LABEL="Available for Cases"
19
+ DEFAULT_CHANNEL_DN="Cases"
20
+ ;;
21
+ Incident)
22
+ AVAILABLE_STATUS_DN="Available_Incident"
23
+ AVAILABLE_STATUS_LABEL="Available for Incidents"
24
+ DEFAULT_CHANNEL_DN="Incidents"
25
+ ;;
26
+ MessagingSession)
27
+ AVAILABLE_STATUS_DN="Available_Messaging"
28
+ AVAILABLE_STATUS_LABEL="Available for Messaging"
29
+ DEFAULT_CHANNEL_DN="sfdc_livemessage"
30
+ ;;
31
+ VoiceCall)
32
+ AVAILABLE_STATUS_DN="Available_Voice"
33
+ AVAILABLE_STATUS_LABEL="Available for Voice"
34
+ DEFAULT_CHANNEL_DN="sfdc_phone"
35
+ ;;
36
+ *)
37
+ echo "{\"error\":\"Unsupported sobject_type: '$SOBJECT_TYPE'. Supported: Case, Incident, MessagingSession, VoiceCall\"}" >&2
38
+ exit 2
39
+ ;;
40
+ esac
41
+
42
+ # Resolve the channel to bind: prefer the coordinator-supplied discovered channel, validate it,
43
+ # else fall back to the canonical default. Never silently ignore a passed-in channel name.
44
+ if [ -n "$CHANNEL_DN_ARG" ]; then
45
+ if ! [[ "$CHANNEL_DN_ARG" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
46
+ echo "{\"error\":\"Invalid channel_developer_name '$CHANNEL_DN_ARG'. A Salesforce DeveloperName must start with a letter (allowed: A-Z a-z, then A-Z a-z 0-9 _; max 80).\"}" >&2
47
+ exit 1
48
+ fi
49
+ BOUND_CHANNEL_DN="$CHANNEL_DN_ARG"
50
+ CHANNEL_SOURCE="discovered"
51
+ else
52
+ BOUND_CHANNEL_DN="$DEFAULT_CHANNEL_DN"
53
+ CHANNEL_SOURCE="default"
54
+ fi
55
+
56
+ if ! sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
57
+ echo "{\"error\":\"Org alias '$ORG' is not authenticated.\"}" >&2
58
+ exit 1
59
+ fi
60
+
61
+ # Write guard: deploys ServicePresenceStatus metadata, so self-enforce the production-org guard (directly invokable).
62
+ ORG_GUARD_JSON=$(sf data query --target-org "$ORG" \
63
+ --query "SELECT IsSandbox, TrialExpirationDate, OrganizationType FROM Organization LIMIT 1" \
64
+ --json 2>/dev/null) || {
65
+ echo "{\"error\":\"Failed to query Organization for safe_to_write guard: $(printf '%s' "$ORG_GUARD_JSON" | head -c 300)\"}" >&2
66
+ exit 1
67
+ }
68
+ IS_SANDBOX=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].IsSandbox')
69
+ TRIAL_EXP=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].TrialExpirationDate // "null"')
70
+ ORG_TYPE=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].OrganizationType')
71
+
72
+ SAFE_TO_WRITE=false
73
+ if [ "$IS_SANDBOX" = "true" ] \
74
+ || [ "$TRIAL_EXP" != "null" ] \
75
+ || [ "$ORG_TYPE" = "Developer Edition" ] \
76
+ || [ "$ORG_TYPE" = "Base Edition" ]; then
77
+ SAFE_TO_WRITE=true
78
+ fi
79
+
80
+ if [ "$SAFE_TO_WRITE" != "true" ]; then
81
+ echo "{\"skill\":\"service-omni-presence-status-deploy\",\"status\":\"blocked\",\"blocking_issue\":\"Refusing to deploy presence statuses to a real production customer org (IsSandbox=$IS_SANDBOX, TrialExpirationDate=$TRIAL_EXP, OrganizationType=$ORG_TYPE). Target a sandbox, trial CDO, or Developer Edition org.\",\"safe_to_write\":false}" >&2
82
+ exit 1
83
+ fi
84
+
85
+ # emit read-only plan preview JSON and exit 0 (PLAN_ONLY mode)
86
+ emit_plan() {
87
+ local status="$1"
88
+ local detail="$2"
89
+ jq -n \
90
+ --arg status "$status" \
91
+ --arg detail "$detail" \
92
+ --arg sobject "$SOBJECT_TYPE" \
93
+ --arg chan "$BOUND_CHANNEL_DN" \
94
+ '{
95
+ skill: "service-omni-presence-status-deploy",
96
+ status: $status,
97
+ plan_mode: true,
98
+ plan_detail: $detail,
99
+ sobject_type: $sobject,
100
+ channel_developer_name: $chan,
101
+ manual_actions: [],
102
+ blocking_issue: null
103
+ }'
104
+ exit 0
105
+ }
106
+
107
+ # PLAN_ONLY: detect the two statuses via SOQL (read-only); the channel binding is verified only in a
108
+ # real run (its junction is not queryable), so plan uses existence as the preview signal.
109
+ if [ "${PLAN_ONLY:-}" = "1" ]; then
110
+ PS_EXIST_JSON=$(sf data query --target-org "$ORG" --json \
111
+ --query "SELECT DeveloperName FROM ServicePresenceStatus WHERE DeveloperName IN ('$AVAILABLE_STATUS_DN','Busy')" 2>/dev/null || echo '{}')
112
+ AVAIL_PRESENT=$(echo "$PS_EXIST_JSON" | jq -r --arg dn "$AVAILABLE_STATUS_DN" '[.result.records[].DeveloperName] | index($dn) != null' 2>/dev/null || echo false)
113
+ BUSY_PRESENT=$(echo "$PS_EXIST_JSON" | jq -r '[.result.records[].DeveloperName] | index("Busy") != null' 2>/dev/null || echo false)
114
+ if [ "$AVAIL_PRESENT" = "true" ] && [ "$BUSY_PRESENT" = "true" ]; then
115
+ emit_plan "reused" "Presence status '$AVAILABLE_STATUS_DN' and a 'Busy' status already exist."
116
+ elif [ "$AVAIL_PRESENT" = "true" ]; then
117
+ emit_plan "action_needed" "'$AVAILABLE_STATUS_DN' exists; would deploy a 'Busy' status (none found)."
118
+ elif [ "$BUSY_PRESENT" = "true" ]; then
119
+ emit_plan "action_needed" "A 'Busy' status exists (reuse); would deploy '$AVAILABLE_STATUS_DN' (→ channel '$BOUND_CHANNEL_DN')."
120
+ else
121
+ emit_plan "action_needed" "Would deploy presence status '$AVAILABLE_STATUS_DN' (→ channel '$BOUND_CHANNEL_DN') and a 'Busy' status."
122
+ fi
123
+ fi
124
+
125
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
126
+ ASSETS_DIR="$(cd "$SCRIPT_DIR/../assets" && pwd)"
127
+
128
+ if [ ! -f "$ASSETS_DIR/sfdx-project.json" ]; then
129
+ echo "{\"error\":\"Assets DX project not found at $ASSETS_DIR\"}" >&2
130
+ exit 1
131
+ fi
132
+
133
+ STATUS_XML="$ASSETS_DIR/force-app/main/default/servicePresenceStatuses/${AVAILABLE_STATUS_DN}.servicePresenceStatus-meta.xml"
134
+ if [ ! -f "$STATUS_XML" ]; then
135
+ echo "{\"error\":\"Asset not found for sobject_type=$SOBJECT_TYPE at $STATUS_XML\"}" >&2
136
+ exit 1
137
+ fi
138
+
139
+ # Materialize into a temp DX project so we can rewrite Available_<X>'s <channel> to the resolved
140
+ # channel. Reuse an existing "Busy" status if present (never redeploy it); deploy the bundled Busy only when absent.
141
+ BUSY_EXIST_JSON=$(sf data query --target-org "$ORG" --json \
142
+ --query "SELECT DeveloperName FROM ServicePresenceStatus WHERE DeveloperName='Busy'" 2>/dev/null || echo '{}')
143
+ BUSY_EXISTS=$(echo "$BUSY_EXIST_JSON" | jq -r '(.result.totalSize // 0) > 0' 2>/dev/null || echo false)
144
+
145
+ WORK="$(mktemp -d)"
146
+ trap 'rm -rf "$WORK"' EXIT
147
+ DEST_DIR="$WORK/force-app/main/default/servicePresenceStatuses"
148
+ mkdir -p "$DEST_DIR"
149
+ cp "$ASSETS_DIR/sfdx-project.json" "$WORK/sfdx-project.json"
150
+
151
+ # Available_<X>: rewrite the single <channel>...</channel> to the resolved channel DN.
152
+ # The XML has exactly one <channel> element (see asset); a targeted sed keeps the rest intact.
153
+ sed -E "s|<channel>[^<]*</channel>|<channel>${BOUND_CHANNEL_DN}</channel>|" \
154
+ "$STATUS_XML" > "$DEST_DIR/${AVAILABLE_STATUS_DN}.servicePresenceStatus-meta.xml"
155
+
156
+ # Build the --metadata deploy list. Always deploy the Available_<X> status; add "Busy" only when it
157
+ # does not already exist on the org.
158
+ DEPLOY_METADATA_ARGS=(--metadata "ServicePresenceStatus:${AVAILABLE_STATUS_DN}")
159
+ if [ "$BUSY_EXISTS" != "true" ]; then
160
+ cp "$ASSETS_DIR/force-app/main/default/servicePresenceStatuses/Busy.servicePresenceStatus-meta.xml" \
161
+ "$DEST_DIR/Busy.servicePresenceStatus-meta.xml"
162
+ DEPLOY_METADATA_ARGS+=(--metadata "ServicePresenceStatus:Busy")
163
+ fi
164
+
165
+ # One atomic deploy, explicit --metadata (no sibling piggyback). Deploy --async + poll so a
166
+ # synchronous ClientTimeoutError under org load can't produce a false red; budget ~20 min (80 * 15s).
167
+ START_JSON=$(cd "$WORK" && sf project deploy start --target-org "$ORG" \
168
+ "${DEPLOY_METADATA_ARGS[@]}" \
169
+ --async --json 2>/dev/null || true)
170
+
171
+ DEPLOY_ID=$(echo "$START_JSON" | jq -r '.result.id // ""' 2>/dev/null)
172
+ DEPLOY_JSON="$START_JSON"
173
+
174
+ if [ -n "$DEPLOY_ID" ] && [ "$DEPLOY_ID" != "null" ]; then
175
+ # Poll to a terminal state (Succeeded/Failed/SucceededPartial/Canceled); "in progress" is not blocked.
176
+ POLL_STATUS=""
177
+ for _ in $(seq 1 80); do
178
+ REPORT_JSON=$(cd "$WORK" && sf project deploy report --job-id "$DEPLOY_ID" --target-org "$ORG" --json 2>/dev/null || true)
179
+ CUR_STATUS=$(echo "$REPORT_JSON" | jq -r '.result.status // ""' 2>/dev/null)
180
+ case "$CUR_STATUS" in
181
+ Succeeded|Failed|SucceededPartial|Canceled)
182
+ DEPLOY_JSON="$REPORT_JSON"; POLL_STATUS="$CUR_STATUS"; break ;;
183
+ esac
184
+ sleep 15
185
+ done
186
+
187
+ if [ -z "$POLL_STATUS" ]; then
188
+ # No terminal state within budget - fail closed (don't report success on an unobserved deploy).
189
+ jq -n \
190
+ --arg deploy_id "$DEPLOY_ID" \
191
+ --arg sobject "$SOBJECT_TYPE" \
192
+ --arg avail_dn "$AVAILABLE_STATUS_DN" \
193
+ --arg avail_lbl "$AVAILABLE_STATUS_LABEL" \
194
+ --arg chan "$BOUND_CHANNEL_DN" \
195
+ '{
196
+ skill: "service-omni-presence-status-deploy",
197
+ status: "blocked",
198
+ sobject_type: $sobject,
199
+ presence_statuses: [
200
+ {developer_name: $avail_dn, label: $avail_lbl, channels: [$chan], state: "deploy_pending"},
201
+ {developer_name: "Busy", label: "Busy", channels: [], state: "deploy_pending"}
202
+ ],
203
+ deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
204
+ manual_actions: [("Inspect deploy: sf project deploy report --job-id " + $deploy_id)],
205
+ blocking_issue: ("Presence-status deploy job " + $deploy_id + " did not reach a terminal state within the ~20 min poll budget (org may still be processing under load). Not reporting success.")
206
+ }'
207
+ exit 1
208
+ fi
209
+ fi
210
+
211
+ # Re-derive from the report JSON (or START_JSON if the async start had no job id → failure branch below).
212
+ DEPLOY_SUCCESS=$(echo "$DEPLOY_JSON" | jq -r '.result.success // false' 2>/dev/null)
213
+ DEPLOY_ID=$(echo "$DEPLOY_JSON" | jq -r '.result.id // null' 2>/dev/null)
214
+
215
+ translate_deploy_error() {
216
+ local raw="$1"
217
+ case "$raw" in
218
+ *INVALID_TYPE*"ServicePresenceStatus"*|*INVALID_TYPE*"OmniChannel"*)
219
+ echo "OmniChannelSettings is not enabled on this org. Run service-omni-base-settings-configure first."
220
+ ;;
221
+ *INVALID_FIELD_VALUE*"$BOUND_CHANNEL_DN"*|*"Value '$BOUND_CHANNEL_DN' is not valid"*)
222
+ echo "ServiceChannel '$BOUND_CHANNEL_DN' does not exist on this org. Run service-omni-service-channel-configure with sobject_type=$SOBJECT_TYPE first."
223
+ ;;
224
+ *"Element {http://soap.sforce.com/2006/04/metadata}statusType"*)
225
+ echo "Deploy XML contains a <statusType> element that is not part of the ServicePresenceStatus schema. Remove <statusType> - online/busy is inferred from <channels> presence."
226
+ ;;
227
+ *)
228
+ echo "$raw"
229
+ ;;
230
+ esac
231
+ }
232
+
233
+ if [ "$DEPLOY_SUCCESS" != "true" ]; then
234
+ FAILURES=$(echo "$DEPLOY_JSON" | jq -r '.result.details.componentFailures // [] | map(.problem) | join("; ")' 2>/dev/null)
235
+ if [ -z "$FAILURES" ] || [ "$FAILURES" = "null" ]; then
236
+ FAILURES=$(echo "$DEPLOY_JSON" | jq -r '.message // .result.errorMessage // "Unknown deploy error"' 2>/dev/null)
237
+ fi
238
+ FRIENDLY=$(translate_deploy_error "$FAILURES")
239
+
240
+ MANUAL_ACTIONS='[]'
241
+ case "$FRIENDLY" in
242
+ *"service-omni-base-settings-configure"*)
243
+ MANUAL_ACTIONS='[{"id":"MISSING_PREREQ","title":"Run prerequisite skill","target_skill":"service-omni-base-settings-configure"}]'
244
+ ;;
245
+ *"service-omni-service-channel-configure"*)
246
+ MANUAL_ACTIONS='[{"id":"MISSING_PREREQ","title":"Run prerequisite skill","target_skill":"service-omni-service-channel-configure","sobject_type":"'"$SOBJECT_TYPE"'"}]'
247
+ ;;
248
+ esac
249
+
250
+ jq -n \
251
+ --arg deploy_id "$DEPLOY_ID" \
252
+ --arg blocking "$FRIENDLY" \
253
+ --arg raw "$FAILURES" \
254
+ --arg sobject "$SOBJECT_TYPE" \
255
+ --arg avail_dn "$AVAILABLE_STATUS_DN" \
256
+ --arg avail_lbl "$AVAILABLE_STATUS_LABEL" \
257
+ --arg chan "$BOUND_CHANNEL_DN" \
258
+ --argjson ma "$MANUAL_ACTIONS" \
259
+ '{
260
+ skill: "service-omni-presence-status-deploy",
261
+ status: "blocked",
262
+ sobject_type: $sobject,
263
+ presence_statuses: [
264
+ {developer_name: $avail_dn, label: $avail_lbl, channels: [$chan], state: "not_deployed"},
265
+ {developer_name: "Busy", label: "Busy", channels: [], state: "not_deployed"}
266
+ ],
267
+ deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
268
+ manual_actions: $ma,
269
+ blocking_issue: $blocking,
270
+ raw_error: $raw
271
+ }'
272
+ exit 1
273
+ fi
274
+
275
+ get_state() {
276
+ local fullname="$1"
277
+ echo "$DEPLOY_JSON" | jq -r --arg fn "$fullname" \
278
+ '.result.files[] | select(.fullName == $fn and .type == "ServicePresenceStatus") | .state' 2>/dev/null | head -1
279
+ }
280
+
281
+ STATE_AVAILABLE=$(get_state "$AVAILABLE_STATUS_DN")
282
+ STATE_AVAILABLE="${STATE_AVAILABLE:-unknown}"
283
+
284
+ # Busy: if it already existed we reused it (never deployed) → treat as Unchanged. Otherwise read
285
+ # its deploy state from the response.
286
+ if [ "$BUSY_EXISTS" = "true" ]; then
287
+ STATE_BUSY="Unchanged"
288
+ else
289
+ STATE_BUSY=$(get_state "Busy")
290
+ STATE_BUSY="${STATE_BUSY:-unknown}"
291
+ fi
292
+
293
+ # Each component's state must be a known idempotency signal (Created/Changed/Unchanged); anything
294
+ # else means we can't prove it deployed - block rather than report success.
295
+ for pair in "AVAILABLE:$STATE_AVAILABLE" "BUSY:$STATE_BUSY"; do
296
+ comp="${pair%%:*}"; st="${pair#*:}"
297
+ case "$st" in
298
+ Created|Changed|Unchanged) ;;
299
+ *)
300
+ jq -n \
301
+ --arg sobject "$SOBJECT_TYPE" \
302
+ --arg avail_dn "$AVAILABLE_STATUS_DN" \
303
+ --arg chan "$BOUND_CHANNEL_DN" \
304
+ --arg deploy_id "$DEPLOY_ID" \
305
+ --arg comp "$comp" \
306
+ --arg st "$st" \
307
+ '{
308
+ skill: "service-omni-presence-status-deploy",
309
+ status: "blocked",
310
+ sobject_type: $sobject,
311
+ presence_statuses: [{developer_name: $avail_dn, channels: [$chan], state: $st}],
312
+ deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
313
+ manual_actions: [],
314
+ blocking_issue: ("Deploy reported success overall but component " + $comp + " has an unrecognized/absent state \"" + $st + "\" (expected Created, Changed, or Unchanged). Not reporting success.")
315
+ }'
316
+ exit 1
317
+ ;;
318
+ esac
319
+ done
320
+
321
+ # Post-verify the channel binding by RETRIEVING the deployed metadata and reading its <channel>
322
+ # back - the ServicePresenceStatusChannel junction is not queryable (INVALID_TYPE). Bounded retry
323
+ # with backoff so transient empty retrieves don't flip to blocked; XML present is authoritative,
324
+ # exhausted budget leaves "unverified" (fail closed below).
325
+ BINDING_VERIFIED="unverified"
326
+ VERIFY_ATTEMPTS=5
327
+ VERIFY_SLEEP=10
328
+ for verify_attempt in $(seq 1 "$VERIFY_ATTEMPTS"); do
329
+ VERIFY_WORK="$(mktemp -d)"
330
+ cp "$ASSETS_DIR/sfdx-project.json" "$VERIFY_WORK/sfdx-project.json"
331
+ mkdir -p "$VERIFY_WORK/force-app/main/default"
332
+ # Retrieve for its side effect (writes XML into VERIFY_WORK); verified by inspecting the file below.
333
+ ( cd "$VERIFY_WORK" && sf project retrieve start --target-org "$ORG" \
334
+ --metadata "ServicePresenceStatus:${AVAILABLE_STATUS_DN}" --json >/dev/null 2>&1 || true )
335
+ RETRIEVED_XML=$(find "$VERIFY_WORK" -name "${AVAILABLE_STATUS_DN}.servicePresenceStatus-meta.xml" 2>/dev/null | head -1)
336
+ if [ -n "$RETRIEVED_XML" ] && [ -f "$RETRIEVED_XML" ]; then
337
+ # Extract every <channel>...</channel> value and test for an exact match to the resolved channel.
338
+ if grep -oE "<channel>[^<]*</channel>" "$RETRIEVED_XML" 2>/dev/null \
339
+ | sed -E 's|<channel>([^<]*)</channel>|\1|' \
340
+ | grep -qxF "$BOUND_CHANNEL_DN"; then
341
+ BINDING_VERIFIED="true"
342
+ else
343
+ BINDING_VERIFIED="false"
344
+ fi
345
+ rm -rf "$VERIFY_WORK"
346
+ break
347
+ fi
348
+ # No XML (likely transient latency) - back off and retry until the budget is exhausted.
349
+ rm -rf "$VERIFY_WORK"
350
+ if [ "$verify_attempt" -lt "$VERIFY_ATTEMPTS" ]; then
351
+ sleep "$VERIFY_SLEEP"
352
+ VERIFY_SLEEP=$((VERIFY_SLEEP + 10))
353
+ fi
354
+ done
355
+
356
+ # Block on both a confirmed-missing binding ("false") and an inconclusive read ("unverified"); a
357
+ # report to STDOUT so the coordinator captures it rather than seeing empty_output.
358
+ if [ "$BINDING_VERIFIED" != "true" ]; then
359
+ if [ "$BINDING_VERIFIED" = "false" ]; then
360
+ BIND_MSG="Deploy reported success but $AVAILABLE_STATUS_DN is not bound to ServiceChannel $BOUND_CHANNEL_DN on post-verify (the retrieved presence-status metadata does not list this channel). The channel DeveloperName may not exist on this org."
361
+ BIND_STATE="deployed_but_unbound"
362
+ else
363
+ BIND_MSG="Deploy reported success but the $AVAILABLE_STATUS_DN <-> $BOUND_CHANNEL_DN binding could not be verified (the metadata retrieve returned no presence-status XML - inconclusive). Not reporting success on an unverified binding."
364
+ BIND_STATE="deployed_binding_unverified"
365
+ fi
366
+ jq -n \
367
+ --arg sobject "$SOBJECT_TYPE" \
368
+ --arg avail_dn "$AVAILABLE_STATUS_DN" \
369
+ --arg chan "$BOUND_CHANNEL_DN" \
370
+ --arg deploy_id "$DEPLOY_ID" \
371
+ --arg bind_state "$BIND_STATE" \
372
+ --arg bind_msg "$BIND_MSG" \
373
+ '{
374
+ skill: "service-omni-presence-status-deploy",
375
+ status: "blocked",
376
+ sobject_type: $sobject,
377
+ presence_statuses: [{developer_name: $avail_dn, channels: [$chan], state: $bind_state}],
378
+ deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
379
+ manual_actions: [],
380
+ blocking_issue: $bind_msg
381
+ }'
382
+ exit 1
383
+ fi
384
+
385
+ # Skill-level status: reused iff BOTH Unchanged; otherwise the "highest" activity wins (created > updated > reused).
386
+ STATUS="reused"
387
+ if [ "$STATE_AVAILABLE" != "Unchanged" ] || [ "$STATE_BUSY" != "Unchanged" ]; then
388
+ # If any component was Created → status: created; else: updated
389
+ if [ "$STATE_AVAILABLE" = "Created" ] || [ "$STATE_BUSY" = "Created" ]; then
390
+ STATUS="created"
391
+ else
392
+ STATUS="updated"
393
+ fi
394
+ fi
395
+
396
+ jq -n \
397
+ --arg status "$STATUS" \
398
+ --arg sobject "$SOBJECT_TYPE" \
399
+ --arg avail_dn "$AVAILABLE_STATUS_DN" \
400
+ --arg avail_lbl "$AVAILABLE_STATUS_LABEL" \
401
+ --arg chan "$BOUND_CHANNEL_DN" \
402
+ --arg state_avail "$STATE_AVAILABLE" \
403
+ --arg state_busy "$STATE_BUSY" \
404
+ --arg deploy_id "$DEPLOY_ID" \
405
+ --arg channel_source "$CHANNEL_SOURCE" \
406
+ --arg binding_verified "$BINDING_VERIFIED" \
407
+ --arg busy_exists "$BUSY_EXISTS" \
408
+ '{
409
+ skill: "service-omni-presence-status-deploy",
410
+ status: $status,
411
+ sobject_type: $sobject,
412
+ channel_developer_name: $chan,
413
+ channel_source: $channel_source,
414
+ binding_verified: ($binding_verified == "true"),
415
+ presence_statuses: [
416
+ {developer_name: $avail_dn, label: $avail_lbl, channels: [$chan], state: $state_avail},
417
+ {developer_name: "Busy", label: "Busy", channels: [], state: $state_busy, reused_existing: ($busy_exists == "true")}
418
+ ],
419
+ deploy_id: $deploy_id,
420
+ manual_actions: [],
421
+ blocking_issue: null
422
+ }'