@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.
- package/package.json +1 -1
- package/skills/agentforce-observe/SKILL.md +32 -4
- package/skills/agentforce-observe/references/ahm-alerts.md +719 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +275 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/README.md +32 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls +75 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls-meta.xml +5 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/interview-answers.json +13 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +10 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/create.json +20 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/update.json +16 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +273 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +236 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/reference-example-set-comment-value.md +132 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +211 -0
- package/skills/dx-devops-project-manage/SKILL.md +197 -0
- package/skills/dx-devops-project-manage/examples/common-workflows.md +197 -0
- package/skills/dx-devops-project-manage/references/cli-commands.md +295 -0
- package/skills/dx-devops-project-manage/scripts/create-project.sh +48 -0
- package/skills/dx-devops-project-manage/scripts/list-projects.sh +51 -0
- package/skills/dx-devops-project-manage/scripts/update-project.sh +96 -0
- package/skills/education-cloud-academic-calendar-generate/SKILL.md +225 -0
- package/skills/education-cloud-academic-calendar-generate/examples/quarter-calendar.json +47 -0
- package/skills/education-cloud-academic-calendar-generate/examples/sample-output.md +57 -0
- package/skills/education-cloud-academic-calendar-generate/examples/semester-calendar.json +54 -0
- package/skills/education-cloud-academic-calendar-generate/references/calendar-systems.md +127 -0
- package/skills/education-cloud-academic-calendar-generate/references/date-validation.md +222 -0
- package/skills/education-cloud-academic-calendar-generate/references/foundation_prerequisites.md +40 -0
- package/skills/education-cloud-academic-calendar-generate/scripts/validate_calendar_dates.py +143 -0
- package/skills/education-cloud-course-catalog-migrate/SKILL.md +321 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas-detail.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/large-catalog-handling.md +42 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/batch_courses.py +36 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_linked_courses.py +51 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_modality_variants.py +48 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/resolve_api_version.py +43 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/split_course_code.py +39 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/validate_completeness.py +54 -0
- package/skills/education-cloud-domain-configure/SKILL.md +234 -0
- package/skills/education-cloud-domain-configure/references/dependent-features.md +198 -0
- package/skills/education-cloud-domain-configure/references/domain-features/academic-operations.md +386 -0
- package/skills/education-cloud-domain-configure/references/domain-features/alumni-relations.md +155 -0
- package/skills/education-cloud-domain-configure/references/domain-features/cross-domain-dependencies.md +18 -0
- package/skills/education-cloud-domain-configure/references/domain-features/industriessettings-toggle-reference.md +43 -0
- package/skills/education-cloud-domain-configure/references/domain-features/mentoring.md +198 -0
- package/skills/education-cloud-domain-configure/references/domain-features/recruitment-admissions.md +315 -0
- package/skills/education-cloud-domain-configure/references/domain-features/student-management.md +155 -0
- package/skills/education-cloud-domain-configure/references/domain-features/student-success.md +249 -0
- package/skills/education-cloud-domain-configure/references/domain-features.md +27 -0
- package/skills/education-cloud-domain-configure/references/gotchas-extended.md +23 -0
- package/skills/education-cloud-domain-configure/references/toggle-write-path.md +67 -0
- package/skills/education-cloud-domain-configure/scripts/check_license_provisioned.py +62 -0
- package/skills/education-cloud-domain-configure/scripts/check_org_edition.py +44 -0
- package/skills/education-cloud-domain-configure/scripts/check_permission_assigned.py +39 -0
- package/skills/education-cloud-multi-campus-configure/SKILL.md +7 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +1 -2
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +177 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/agent-and-subagents.md +151 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/customer-narration.md +34 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/execution-model.md +54 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/flows.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/grounding.md +199 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/permissions.md +183 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/platform-enablement.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +158 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/routing.md +141 -0
- package/skills/experience-cms-brand-apply/SKILL.md +5 -5
- package/skills/experience-cms-brand-create/SKILL.md +2 -2
- package/skills/experience-cms-content-generate/SKILL.md +1 -0
- package/skills/experience-cms-content-render/SKILL.md +173 -0
- package/skills/experience-cms-content-render/assets/angular/DetailPage.component.ts +25 -0
- package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +133 -0
- package/skills/experience-cms-content-render/assets/angular/TypeList.component.ts +38 -0
- package/skills/experience-cms-content-render/assets/angular/TypeRenderer.component.ts +90 -0
- package/skills/experience-cms-content-render/assets/angular/cms-content.component.ts +248 -0
- package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +100 -0
- package/skills/experience-cms-content-render/assets/react/DetailPage.tsx +20 -0
- package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +129 -0
- package/skills/experience-cms-content-render/assets/react/TypeList.tsx +40 -0
- package/skills/experience-cms-content-render/assets/react/TypeRenderer.tsx +64 -0
- package/skills/experience-cms-content-render/assets/react/heuristicRenderer.tsx +310 -0
- package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +129 -0
- package/skills/experience-cms-content-render/assets/shared/cmsContentType.ts +49 -0
- package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +96 -0
- package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +55 -0
- package/skills/experience-cms-content-render/references/bulk-loading.md +60 -0
- package/skills/experience-cms-content-render/references/codegen-guardrails.md +111 -0
- package/skills/experience-cms-content-render/references/detail-pages.md +87 -0
- package/skills/experience-cms-content-render/references/embed-recipes.md +127 -0
- package/skills/experience-cms-content-render/references/failure-modes.md +96 -0
- package/skills/experience-cms-content-render/references/heuristic-render-rules.md +131 -0
- package/skills/experience-cms-content-render/references/init-scaffold.md +122 -0
- package/skills/experience-cms-content-render/references/interaction-model.md +173 -0
- package/skills/experience-cms-content-render/references/package-api.md +106 -0
- package/skills/experience-cms-content-render/references/schema-sync.md +114 -0
- package/skills/experience-cms-content-render/references/styling-scopes.md +65 -0
- package/skills/experience-cms-content-render/references/verify.md +49 -0
- package/skills/experience-cms-content-type-generate/SKILL.md +2 -2
- package/skills/experience-content-media-stock-image-search/SKILL.md +5 -4
- package/skills/experience-search-coordinate/SKILL.md +198 -0
- package/skills/experience-search-coordinate/assets/search-payload-template.json +25 -0
- package/skills/experience-search-coordinate/references/content-route.md +313 -0
- package/skills/experience-search-coordinate/references/content-type-discovery.md +57 -0
- package/skills/experience-search-coordinate/references/media-route.md +172 -0
- package/skills/experience-search-coordinate/references/scope-resolution.md +14 -0
- package/skills/experience-ui-bundle-localize/SKILL.md +1 -1
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +5 -3
- package/skills/experience-ui-bundle-project-generate/SKILL.md +18 -14
- package/skills/experience-ui-bundle-project-generate/references/angular-project-generate.md +22 -0
- package/skills/experience-ui-bundle-project-generate/references/react-project-generate.md +20 -0
- package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +58 -54
- package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/graphiti-cli.md +2 -2
- package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/rest-and-integration.md +2 -1
- package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +6 -0
- package/skills/experience-ui-bundle-site-generate/SKILL.md +59 -8
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-digital-experience.md +8 -3
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-language-settings.md +120 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +336 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +143 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +127 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-deploy-commands.md +116 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +111 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-overview.md +312 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +171 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-state-tracking.md +64 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-trigger-handlers.md +122 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-overview.md +335 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-user-provisioning-details.md +140 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +196 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +307 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +211 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/state-machine-and-changes.md +108 -0
- package/skills/life-sciences-kam-coordinate/SKILL.md +241 -0
- package/skills/life-sciences-kam-coordinate/references/orchestration-flow.md +152 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +79 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-deploy-commands.md +131 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-kam-config-records.md +85 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +112 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-overview.md +202 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +67 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-state-tracking.md +65 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-trigger-handlers.md +123 -0
- package/skills/life-sciences-kam-coordinate/references/stage-4-participant-role-and-sprint.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +337 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +248 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-ipad-validation-script.md +35 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-details.md +146 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-overview.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/state-machine-and-changes.md +114 -0
- package/skills/life-sciences-prerequisites-validate/SKILL.md +138 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-org-settings.md +190 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-user-and-package.md +211 -0
- package/skills/life-sciences-territory-configure/SKILL.md +217 -0
- package/skills/life-sciences-territory-configure/references/territory-metadata.md +262 -0
- package/skills/platform-dsar-policy-manage/SKILL.md +272 -0
- package/skills/platform-dsar-policy-manage/references/configure.md +106 -0
- package/skills/platform-dsar-policy-manage/references/export-and-history.md +123 -0
- package/skills/platform-dsar-policy-manage/references/gap-analysis-guide.md +150 -0
- package/skills/platform-dsar-policy-manage/references/gap-scan.md +129 -0
- package/skills/platform-dsar-policy-manage/references/headless-sor.md +59 -0
- package/skills/platform-dsar-policy-manage/references/report-format.md +59 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/__init__.py +0 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/test_validate_policy_tree.py +76 -0
- package/skills/platform-dsar-policy-manage/scripts/validate-policy-tree.py +130 -0
- package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +359 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/official-examples.md +69 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/scenarios.md +187 -0
- package/skills/service-agentforce-channel-configure/SKILL.md +33 -28
- package/skills/service-agentforce-human-escalation-configure/SKILL.md +205 -0
- package/skills/service-agentforce-human-escalation-configure/assets/escalation-thresholds.instructions.md +37 -0
- package/skills/service-agentforce-human-escalation-configure/assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml +173 -0
- package/skills/service-agentforce-human-escalation-configure/assets/package.xml +8 -0
- package/skills/service-agentforce-human-escalation-configure/assets/sfdx-project.json +5 -0
- package/skills/service-agentforce-human-escalation-configure/references/classifier-contracts.md +103 -0
- package/skills/service-agentforce-human-escalation-configure/references/escalation-mechanism.md +63 -0
- package/skills/service-agentforce-human-escalation-configure/references/failure-policy.md +44 -0
- package/skills/service-agentforce-human-escalation-configure/references/nga-escalation.md +57 -0
- package/skills/service-agentforce-human-escalation-configure/references/runtime-verification.md +72 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/classify-agent-active.mjs +49 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/classify-nga-escalation.mjs +166 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/extract-outbound-route.mjs +67 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/patch-escalation-surfaces.mjs +131 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/__init__.py +0 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +249 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +899 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/verify-and-configure.sh +705 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/verify-escalation-config.mjs +89 -0
- package/skills/service-de-channel-routing-configure/SKILL.md +13 -5
- package/skills/service-de-channel-routing-configure/scripts/tests/test_skill_contract.py +25 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +23 -10
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -48
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +6 -2
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +20 -27
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +47 -27
- package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +13 -9
- package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +104 -45
- package/skills/service-omni-agent-users-create/SKILL.md +96 -0
- package/skills/service-omni-agent-users-create/assets/create-users.apex.template +80 -0
- package/skills/service-omni-agent-users-create/references/apex-patterns.md +135 -0
- package/skills/service-omni-agent-users-create/references/apex-template-notes.md +40 -0
- package/skills/service-omni-agent-users-create/scripts/detect-and-create.sh +206 -0
- package/skills/service-omni-agent-users-create/scripts/detect-existing.sh +122 -0
- package/skills/service-omni-agent-users-create/scripts/run-create.sh +298 -0
- package/skills/service-omni-agent-users-create/scripts/tests/test_user_create_security.py +171 -0
- package/skills/service-omni-agent-work-sharing-configure/SKILL.md +78 -0
- package/skills/service-omni-agent-work-sharing-configure/references/api-notes.md +31 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/configure-and-report.sh +186 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/tests/_bootstrap.py +120 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/tests/test_agent_work_sharing_contracts.py +93 -0
- package/skills/service-omni-base-settings-configure/SKILL.md +77 -0
- package/skills/service-omni-base-settings-configure/assets/force-app/main/default/settings/OmniChannel.settings-meta.xml +8 -0
- package/skills/service-omni-base-settings-configure/assets/package.xml +8 -0
- package/skills/service-omni-base-settings-configure/assets/sfdx-project.json +12 -0
- package/skills/service-omni-base-settings-configure/references/api-notes.md +121 -0
- package/skills/service-omni-base-settings-configure/scripts/configure-and-report.sh +164 -0
- package/skills/service-omni-base-settings-configure/scripts/detect-existing.sh +69 -0
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +168 -0
- package/skills/service-omni-channel-setup-coordinate/examples/output-templates.md +161 -0
- package/skills/service-omni-channel-setup-coordinate/references/gap-catalog.md +99 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +1542 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/__init__.py +0 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/_bootstrap.py +223 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +804 -0
- package/skills/service-omni-command-center-analyze/SKILL.md +84 -0
- package/skills/service-omni-command-center-analyze/references/api-notes.md +54 -0
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +198 -0
- package/skills/service-omni-command-center-analyze/scripts/tests/_bootstrap.py +115 -0
- package/skills/service-omni-command-center-analyze/scripts/tests/test_command_center_analyze_contracts.py +165 -0
- package/skills/service-omni-permission-set-assign/SKILL.md +88 -0
- package/skills/service-omni-permission-set-assign/assets/package.xml +8 -0
- package/skills/service-omni-permission-set-assign/references/api-notes.md +143 -0
- package/skills/service-omni-permission-set-assign/scripts/verify-and-assign.sh +397 -0
- package/skills/service-omni-presence-status-deploy/SKILL.md +87 -0
- package/skills/service-omni-presence-status-deploy/assets/package.xml +12 -0
- package/skills/service-omni-presence-status-deploy/references/api-notes.md +115 -0
- package/skills/service-omni-presence-status-deploy/scripts/deploy-and-report.sh +422 -0
- package/skills/service-omni-presence-user-config-deploy/SKILL.md +80 -0
- package/skills/service-omni-presence-user-config-deploy/references/api-notes.md +60 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/deploy-and-report.sh +261 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/tests/_bootstrap.py +106 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/tests/test_presence_user_config_contracts.py +142 -0
- package/skills/service-omni-queue-deploy/SKILL.md +91 -0
- package/skills/service-omni-queue-deploy/references/api-notes.md +140 -0
- package/skills/service-omni-queue-deploy/scripts/verify-and-align.sh +470 -0
- package/skills/service-omni-queue-members-assign/SKILL.md +93 -0
- package/skills/service-omni-queue-members-assign/references/api-notes.md +144 -0
- package/skills/service-omni-queue-members-assign/scripts/tests/_bootstrap.py +67 -0
- package/skills/service-omni-queue-members-assign/scripts/tests/test_queue_members_contracts.py +73 -0
- package/skills/service-omni-queue-members-assign/scripts/verify-and-bind.sh +362 -0
- package/skills/service-omni-queue-routing-config-deploy/SKILL.md +101 -0
- package/skills/service-omni-queue-routing-config-deploy/references/api-notes.md +127 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/tests/_bootstrap.py +90 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/tests/test_queue_routing_config_contracts.py +70 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/upsert-and-report.sh +538 -0
- package/skills/service-omni-routing-flow-deploy/SKILL.md +111 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.SkillsBased.flow-meta.xml +94 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.flow-meta.xml +88 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Cases.flow-meta.xml +144 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.SkillsBased.flow-meta.xml +94 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.flow-meta.xml +88 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCalls.flow-meta.xml +144 -0
- package/skills/service-omni-routing-flow-deploy/assets/package.xml +11 -0
- package/skills/service-omni-routing-flow-deploy/assets/sfdx-project.json +11 -0
- package/skills/service-omni-routing-flow-deploy/references/api-notes.md +265 -0
- package/skills/service-omni-routing-flow-deploy/scripts/deploy-and-report.sh +684 -0
- package/skills/service-omni-service-channel-configure/SKILL.md +83 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Cases.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Incidents.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/MessagingSessions.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/VoiceCalls.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/package.xml +11 -0
- package/skills/service-omni-service-channel-configure/assets/sfdx-project.json +12 -0
- package/skills/service-omni-service-channel-configure/references/api-notes.md +84 -0
- package/skills/service-omni-service-channel-configure/scripts/deploy-and-report.sh +383 -0
- package/skills/service-omni-sidebar-configure/SKILL.md +77 -0
- package/skills/service-omni-sidebar-configure/references/api-notes.md +51 -0
- package/skills/service-omni-sidebar-configure/scripts/enable-and-report.sh +315 -0
- package/skills/service-omni-sidebar-configure/scripts/tests/_bootstrap.py +142 -0
- package/skills/service-omni-sidebar-configure/scripts/tests/test_sidebar_contracts.py +139 -0
- package/skills/service-omni-skills-based-routing-configure/SKILL.md +81 -0
- package/skills/service-omni-skills-based-routing-configure/references/api-notes.md +50 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/configure-and-report.sh +270 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/tests/_bootstrap.py +120 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/tests/test_sbr_contracts.py +129 -0
- package/skills/service-omni-supervisor-config-deploy/SKILL.md +97 -0
- package/skills/service-omni-supervisor-config-deploy/assets/force-app/main/default/omniSupervisorConfigs/Omni_Supervisor.omniSupervisorConfig-meta.xml +9 -0
- package/skills/service-omni-supervisor-config-deploy/assets/package.xml +8 -0
- package/skills/service-omni-supervisor-config-deploy/assets/sfdx-project.json +12 -0
- package/skills/service-omni-supervisor-config-deploy/references/api-notes.md +205 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/deploy-and-report.sh +586 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/tests/_bootstrap.py +153 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/tests/test_supervisor_config_contracts.py +116 -0
- package/skills/service-omni-supervisor-permset-assign/SKILL.md +88 -0
- package/skills/service-omni-supervisor-permset-assign/references/api-notes.md +48 -0
- package/skills/service-omni-supervisor-permset-assign/scripts/verify-and-assign.sh +316 -0
- package/skills/service-omni-supervisor-surface-deploy/SKILL.md +85 -0
- package/skills/service-omni-supervisor-surface-deploy/references/api-notes.md +47 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/deploy-and-report.sh +216 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/tests/_bootstrap.py +95 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/tests/test_supervisor_surface_contracts.py +154 -0
- package/skills/service-omni-supervisor-users-create/SKILL.md +92 -0
- package/skills/service-omni-supervisor-users-create/assets/create-supervisors.apex.template +72 -0
- package/skills/service-omni-supervisor-users-create/references/apex-patterns.md +73 -0
- package/skills/service-omni-supervisor-users-create/references/apex-template-notes.md +41 -0
- package/skills/service-omni-supervisor-users-create/scripts/detect-and-create.sh +197 -0
- package/skills/service-omni-supervisor-users-create/scripts/detect-existing.sh +112 -0
- package/skills/service-omni-supervisor-users-create/scripts/run-create.sh +296 -0
- package/skills/service-omni-work-skill-routing-configure/SKILL.md +80 -0
- package/skills/service-omni-work-skill-routing-configure/references/api-notes.md +67 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/configure-and-report.sh +262 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/tests/_bootstrap.py +121 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/tests/test_work_skill_routing_contracts.py +161 -0
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-and-configure.sh - configure + verify Agentforce agent → human escalation (queue/QRC/member/flow + doc-driven canEscalate/outboundRoute verify); idempotent, PLAN_ONLY=1 previews. Inputs, ownership, exit codes: SKILL.md + references/.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SKILL_NAME="service-agentforce-human-escalation-configure"
|
|
7
|
+
API_VERSION="v66.0"
|
|
8
|
+
# Runtime flow developer name (env-overridable); the temp deploy is written as ${FLOW_DEV_NAME}.
|
|
9
|
+
FLOW_DEV_NAME="${FLOW_DEVELOPER_NAME:-Human_Escalation_Outbound_Flow}"
|
|
10
|
+
# Human-readable Flow label; defaults to the developer name with underscores as spaces.
|
|
11
|
+
FLOW_LABEL="${FLOW_LABEL:-${FLOW_DEV_NAME//_/ }}"
|
|
12
|
+
# Work object routed to the queue (default MessagingSession; Case/VoiceCall pair with SERVICE_CHANNEL_*).
|
|
13
|
+
CONTEXT_OBJECT="${CONTEXT_OBJECT:-MessagingSession}"
|
|
14
|
+
SERVICE_CHANNEL_DEV_NAME="${SERVICE_CHANNEL_DEV_NAME:-sfdc_livemessage}"
|
|
15
|
+
SERVICE_CHANNEL_LABEL="${SERVICE_CHANNEL_LABEL:-Messaging}"
|
|
16
|
+
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
# Bundled flow is a tokenized template; rendered into ${FLOW_DEV_NAME}.flow-meta.xml at deploy time.
|
|
19
|
+
ASSET_FLOW="$SCRIPT_DIR/../assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml"
|
|
20
|
+
|
|
21
|
+
# Escape the five XML predefined entities, then sed replacement metacharacters, so a caller-supplied
|
|
22
|
+
# FLOW_LABEL / SERVICE_CHANNEL_LABEL with & < > " ' | cannot corrupt the rendered flow XML.
|
|
23
|
+
xml_escape() {
|
|
24
|
+
local s="$1"
|
|
25
|
+
s="${s//&/&}"; s="${s//</<}"; s="${s//>/>}"; s="${s//\"/"}"; s="${s//\'/'}"
|
|
26
|
+
printf '%s' "$s"
|
|
27
|
+
}
|
|
28
|
+
sed_repl_escape() { printf '%s' "$1" | sed -e 's/[\\&|]/\\&/g'; }
|
|
29
|
+
|
|
30
|
+
# Cleanup trap: remove temp working dirs on exit.
|
|
31
|
+
TWORK=""
|
|
32
|
+
_cleanup() { [ -n "$TWORK" ] && rm -rf "$TWORK" 2>/dev/null || true; }
|
|
33
|
+
trap _cleanup EXIT
|
|
34
|
+
|
|
35
|
+
# Args
|
|
36
|
+
if [ $# -lt 1 ]; then
|
|
37
|
+
echo '{"error":"Usage: bash verify-and-configure.sh <org-alias> [<agent-developer-name>] [<queue-developer-name>] [<escalation-topic-api-name>] [<planner-bundle-api-name>]"}' >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
ORG="$1"
|
|
42
|
+
AGENT_DN="${2:-${AGENT_DEVELOPER_NAME:-Support_Agent}}"
|
|
43
|
+
QUEUE_DN="${3:-${QUEUE_DEVELOPER_NAME:-Human_Support_Queue}}"
|
|
44
|
+
ESCALATION_TOPIC="${4:-${ESCALATION_TOPIC_API_NAME:-}}"
|
|
45
|
+
PLANNER_BUNDLE="${5:-${PLANNER_BUNDLE_API_NAME:-}}"
|
|
46
|
+
THRESHOLD_AUTHORED="${THRESHOLD_AUTHORED:-0}"
|
|
47
|
+
# Consecutive failed attempts before hand-off (default 2); rendered into the directive + verdict.
|
|
48
|
+
DEFAULT_FAILURE_THRESHOLD="${DEFAULT_FAILURE_THRESHOLD:-2}"
|
|
49
|
+
|
|
50
|
+
# Input contract - validate BEFORE any sf call. DeveloperName grammar: leading letter, then
|
|
51
|
+
# letters/digits/underscore, max 80.
|
|
52
|
+
DN_RE='^[A-Za-z][A-Za-z0-9_]{0,79}$'
|
|
53
|
+
if ! [[ "$AGENT_DN" =~ $DN_RE ]]; then
|
|
54
|
+
echo "{\"error\":\"Invalid agent_developer_name '$AGENT_DN'. Must start with a letter and contain only letters, digits, and underscores (max 80 chars).\"}" >&2
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
if ! [[ "$QUEUE_DN" =~ $DN_RE ]]; then
|
|
58
|
+
echo "{\"error\":\"Invalid queue_developer_name '$QUEUE_DN'. Must start with a letter and contain only letters, digits, and underscores (max 80 chars).\"}" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
if [ -n "$ESCALATION_TOPIC" ] && ! [[ "$ESCALATION_TOPIC" =~ $DN_RE ]]; then
|
|
62
|
+
echo "{\"error\":\"Invalid escalation_topic_api_name '$ESCALATION_TOPIC'. Must match the DeveloperName grammar.\"}" >&2
|
|
63
|
+
exit 1
|
|
64
|
+
fi
|
|
65
|
+
if [ -n "$PLANNER_BUNDLE" ] && ! [[ "$PLANNER_BUNDLE" =~ $DN_RE ]]; then
|
|
66
|
+
echo "{\"error\":\"Invalid planner_bundle_api_name '$PLANNER_BUNDLE'. Must match the DeveloperName grammar.\"}" >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
# CONTEXT_OBJECT is an sObject API name; validate with the same grammar (safe for SOQL/XML interpolation).
|
|
70
|
+
if ! [[ "$CONTEXT_OBJECT" =~ $DN_RE ]]; then
|
|
71
|
+
echo "{\"error\":\"Invalid context_object '$CONTEXT_OBJECT'. Must be an sObject API name (leading letter, then letters, digits, and underscores).\"}" >&2
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
if ! [[ "$FLOW_DEV_NAME" =~ $DN_RE ]]; then
|
|
75
|
+
echo "{\"error\":\"Invalid flow_developer_name '$FLOW_DEV_NAME'. Must match the DeveloperName grammar.\"}" >&2
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
# DEFAULT_FAILURE_THRESHOLD must be a small positive integer (1-99).
|
|
79
|
+
if ! [[ "$DEFAULT_FAILURE_THRESHOLD" =~ ^[1-9][0-9]?$ ]]; then
|
|
80
|
+
echo "{\"error\":\"Invalid DEFAULT_FAILURE_THRESHOLD '$DEFAULT_FAILURE_THRESHOLD'. Must be a positive integer between 1 and 99.\"}" >&2
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# QUEUE_MEMBER_USERNAMES (optional): parse/trim/validate the full list before any sf call, so bad
|
|
85
|
+
# input never mutates the org first. The cleaned, comma-joined list is what the members step consumes.
|
|
86
|
+
UNAME_RE='^[A-Za-z0-9._%+@-]+$'
|
|
87
|
+
QUEUE_MEMBER_USERNAMES_CLEAN=""
|
|
88
|
+
if [ -n "${QUEUE_MEMBER_USERNAMES:-}" ]; then
|
|
89
|
+
IFS=',' read -ra _UNAMES <<< "$QUEUE_MEMBER_USERNAMES"
|
|
90
|
+
for _raw in "${_UNAMES[@]}"; do
|
|
91
|
+
uname="${_raw#"${_raw%%[![:space:]]*}"}"; uname="${uname%"${uname##*[![:space:]]}"}"
|
|
92
|
+
[ -z "$uname" ] && continue
|
|
93
|
+
if ! [[ "$uname" =~ $UNAME_RE ]]; then
|
|
94
|
+
echo "{\"error\":\"Invalid QUEUE_MEMBER_USERNAMES entry '$uname'. Usernames may contain only letters, digits, and . _ % + @ - characters.\"}" >&2
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
if [ -z "$QUEUE_MEMBER_USERNAMES_CLEAN" ]; then
|
|
98
|
+
QUEUE_MEMBER_USERNAMES_CLEAN="$uname"
|
|
99
|
+
else
|
|
100
|
+
QUEUE_MEMBER_USERNAMES_CLEAN="$QUEUE_MEMBER_USERNAMES_CLEAN,$uname"
|
|
101
|
+
fi
|
|
102
|
+
done
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if ! sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
|
|
106
|
+
echo "{\"error\":\"Org alias '$ORG' is not authenticated. Run: sf org login web -a $ORG -r <my-domain-url>\"}" >&2
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# Helpers
|
|
111
|
+
emit_blocked() {
|
|
112
|
+
local msg="$1"; local click_path="${2:-}"
|
|
113
|
+
local manual_actions='[]'
|
|
114
|
+
if [ -n "$click_path" ]; then
|
|
115
|
+
manual_actions=$(jq -n --arg cp "$click_path" '[{id:"MANUAL_FIX", title:"Manual fix required", click_path:$cp}]')
|
|
116
|
+
fi
|
|
117
|
+
jq -n \
|
|
118
|
+
--arg skill "$SKILL_NAME" --arg msg "$msg" --arg agent "$AGENT_DN" --arg queue "$QUEUE_DN" \
|
|
119
|
+
--argjson ma "$manual_actions" \
|
|
120
|
+
'{skill:$skill, status:"BLOCKED", agent:{developer_name:$agent}, queue:{developer_name:$queue},
|
|
121
|
+
manual_actions:$ma, blocking_issue:$msg}'
|
|
122
|
+
exit 1
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
emit_plan() {
|
|
126
|
+
local detail="$1"
|
|
127
|
+
jq -n --arg skill "$SKILL_NAME" --arg detail "$detail" --arg agent "$AGENT_DN" --arg queue "$QUEUE_DN" \
|
|
128
|
+
'{skill:$skill, status:"PLAN", plan_mode:true, plan_detail:$detail,
|
|
129
|
+
agent:{developer_name:$agent}, queue:{developer_name:$queue}, manual_actions:[], blocking_issue:null}'
|
|
130
|
+
exit 0
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# Step 1 - Organization + safe_to_write guard
|
|
134
|
+
ORG_JSON=$(sf data query --target-org "$ORG" \
|
|
135
|
+
--query "SELECT Id, IsSandbox, TrialExpirationDate, OrganizationType FROM Organization LIMIT 1" \
|
|
136
|
+
--json 2>/dev/null)
|
|
137
|
+
|
|
138
|
+
IS_SANDBOX=$(echo "$ORG_JSON" | jq -r '.result.records[0].IsSandbox')
|
|
139
|
+
TRIAL_EXP=$(echo "$ORG_JSON" | jq -r '.result.records[0].TrialExpirationDate // "null"')
|
|
140
|
+
ORG_TYPE=$(echo "$ORG_JSON" | jq -r '.result.records[0].OrganizationType')
|
|
141
|
+
|
|
142
|
+
SAFE_TO_WRITE=false
|
|
143
|
+
if [ "$IS_SANDBOX" = "true" ]; then
|
|
144
|
+
SAFE_TO_WRITE=true
|
|
145
|
+
elif [ "$TRIAL_EXP" != "null" ]; then
|
|
146
|
+
SAFE_TO_WRITE=true
|
|
147
|
+
elif [ "$ORG_TYPE" = "Developer Edition" ] || [ "$ORG_TYPE" = "Base Edition" ]; then
|
|
148
|
+
SAFE_TO_WRITE=true
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
if [ "$SAFE_TO_WRITE" != "true" ]; then
|
|
152
|
+
emit_blocked "Refusing to configure escalation on a real production customer org (IsSandbox=$IS_SANDBOX, TrialExpirationDate=$TRIAL_EXP, OrganizationType=$ORG_TYPE). Target a sandbox, trial CDO, or Developer Edition org."
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Step 2 - Precondition: agent exists AND latest version Active
|
|
156
|
+
BOT_JSON=$(sf data query --target-org "$ORG" \
|
|
157
|
+
--query "SELECT Id,DeveloperName,MasterLabel,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE DeveloperName='$AGENT_DN'" \
|
|
158
|
+
--json 2>/dev/null)
|
|
159
|
+
|
|
160
|
+
TWORK=$(mktemp -d)
|
|
161
|
+
echo "$BOT_JSON" > "$TWORK/bot.json"
|
|
162
|
+
AGENT_VERDICT=$(node "$SCRIPT_DIR/classify-agent-active.mjs" "$TWORK/bot.json" "$AGENT_DN") || {
|
|
163
|
+
emit_blocked "Could not read BotDefinition for '$AGENT_DN' - surface the raw sf error and retry. Raw: $BOT_JSON"
|
|
164
|
+
}
|
|
165
|
+
AGENT_READY=$(echo "$AGENT_VERDICT" | jq -r '.ready')
|
|
166
|
+
if [ "$AGENT_READY" != "true" ]; then
|
|
167
|
+
AGENT_REASON=$(echo "$AGENT_VERDICT" | jq -r '.reason')
|
|
168
|
+
emit_blocked "Agent precondition failed ($AGENT_REASON). Create and activate the agent '$AGENT_DN' before wiring escalation." \
|
|
169
|
+
"Setup → Agentforce → Agents → '$AGENT_DN' → activate the latest version"
|
|
170
|
+
fi
|
|
171
|
+
AGENT_ACTIVE=true
|
|
172
|
+
|
|
173
|
+
# Step 3 - Precondition: for MessagingSession handoff, an active Enhanced Chat / MIAW MessagingChannel
|
|
174
|
+
# must exist. Other context objects use a different transport and aren't gated here.
|
|
175
|
+
if [ "$CONTEXT_OBJECT" = "MessagingSession" ]; then
|
|
176
|
+
MC_JSON=$(sf data query --target-org "$ORG" \
|
|
177
|
+
--query "SELECT Id, DeveloperName FROM MessagingChannel WHERE IsActive=true LIMIT 1" \
|
|
178
|
+
--json 2>/dev/null)
|
|
179
|
+
MC_COUNT=$(echo "$MC_JSON" | jq -r '.result.totalSize // 0' 2>/dev/null || echo "0")
|
|
180
|
+
if [ "$MC_COUNT" -eq 0 ]; then
|
|
181
|
+
emit_blocked "No active MessagingChannel (Enhanced Chat / MIAW) found. Provision the messaging channel first, then re-run." \
|
|
182
|
+
"Setup → Messaging Settings → New Channel (Messaging for In-App and Web)"
|
|
183
|
+
fi
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# Step 3b - Preflight: determine which Agentforce authoring model the org exposes. classic =
|
|
187
|
+
# GenAiPlugin + GenAiPlannerBundle (canEscalate + outboundRouteConfigs surfaces). nga = AiAuthoringBundle
|
|
188
|
+
# (Agent Script; escalation via @utils.escalate/create-record, routed through channel Omni to the queue).
|
|
189
|
+
# none = neither -> fail closed. Model rationale: references/nga-escalation.md.
|
|
190
|
+
MDT_JSON=$(sf org list metadata-types --target-org "$ORG" --json 2>/dev/null || echo '{}')
|
|
191
|
+
HAS_PLUGIN=$(echo "$MDT_JSON" | jq -r '[.result.metadataObjects[]?.xmlName] | index("GenAiPlugin") // empty' 2>/dev/null || echo "")
|
|
192
|
+
HAS_BUNDLE=$(echo "$MDT_JSON" | jq -r '[.result.metadataObjects[]?.xmlName] | index("GenAiPlannerBundle") // empty' 2>/dev/null || echo "")
|
|
193
|
+
HAS_AIAUTHORING=$(echo "$MDT_JSON" | jq -r '[.result.metadataObjects[]?.xmlName] | index("AiAuthoringBundle") // empty' 2>/dev/null || echo "")
|
|
194
|
+
|
|
195
|
+
AUTHORING_MODEL="none"
|
|
196
|
+
HAS_CLASSIC_SURFACE=false
|
|
197
|
+
HAS_NGA_SURFACE=false
|
|
198
|
+
[ -n "$HAS_BUNDLE" ] && [ -n "$HAS_PLUGIN" ] && HAS_CLASSIC_SURFACE=true
|
|
199
|
+
[ -n "$HAS_AIAUTHORING" ] && HAS_NGA_SURFACE=true
|
|
200
|
+
|
|
201
|
+
# When an org exposes both authoring generations, classify the selected target rather
|
|
202
|
+
# than defaulting the whole org to classic. A target-specific retrieve is read-only and
|
|
203
|
+
# prevents an NGA agent from being verified against unrelated classic metadata.
|
|
204
|
+
TARGET_CLASSIC=false
|
|
205
|
+
TARGET_NGA=false
|
|
206
|
+
if [ "$HAS_NGA_SURFACE" = "true" ]; then
|
|
207
|
+
MODEL_DIR="$TWORK/model-nga"
|
|
208
|
+
mkdir -p "$MODEL_DIR"
|
|
209
|
+
if sf project retrieve start --target-org "$ORG" --metadata "AiAuthoringBundle:$AGENT_DN" \
|
|
210
|
+
--target-metadata-dir "$MODEL_DIR" --unzip --json >/dev/null 2>&1 \
|
|
211
|
+
&& find "$MODEL_DIR" -type f -print -quit | grep -q .; then
|
|
212
|
+
TARGET_NGA=true
|
|
213
|
+
fi
|
|
214
|
+
fi
|
|
215
|
+
if [ "$HAS_CLASSIC_SURFACE" = "true" ]; then
|
|
216
|
+
MODEL_DIR="$TWORK/model-classic"
|
|
217
|
+
mkdir -p "$MODEL_DIR"
|
|
218
|
+
CLASSIC_TARGET="${PLANNER_BUNDLE:-$AGENT_DN}"
|
|
219
|
+
if sf project retrieve start --target-org "$ORG" --metadata "GenAiPlannerBundle:$CLASSIC_TARGET" \
|
|
220
|
+
--target-metadata-dir "$MODEL_DIR" --unzip --json >/dev/null 2>&1 \
|
|
221
|
+
&& find "$MODEL_DIR" -type f -print -quit | grep -q .; then
|
|
222
|
+
TARGET_CLASSIC=true
|
|
223
|
+
fi
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
if [ "$TARGET_NGA" = "true" ] && [ "$TARGET_CLASSIC" != "true" ]; then
|
|
227
|
+
AUTHORING_MODEL="nga"
|
|
228
|
+
elif [ "$TARGET_CLASSIC" = "true" ] && [ "$TARGET_NGA" != "true" ]; then
|
|
229
|
+
AUTHORING_MODEL="classic"
|
|
230
|
+
elif [ "$TARGET_CLASSIC" = "true" ] && [ "$TARGET_NGA" = "true" ]; then
|
|
231
|
+
if [ -z "${AUTHORING_MODEL_OVERRIDE:-}" ]; then
|
|
232
|
+
emit_blocked "The selected agent resolves in both classic and next-gen Agentforce authoring metadata. Set AUTHORING_MODEL_OVERRIDE=classic or nga after confirming the intended target model."
|
|
233
|
+
fi
|
|
234
|
+
elif [ "$HAS_CLASSIC_SURFACE" = "true" ] && [ "$HAS_NGA_SURFACE" != "true" ]; then
|
|
235
|
+
AUTHORING_MODEL="classic"
|
|
236
|
+
elif [ "$HAS_NGA_SURFACE" = "true" ] && [ "$HAS_CLASSIC_SURFACE" != "true" ]; then
|
|
237
|
+
AUTHORING_MODEL="nga"
|
|
238
|
+
elif [ "$HAS_CLASSIC_SURFACE" = "true" ] && [ "$HAS_NGA_SURFACE" = "true" ]; then
|
|
239
|
+
if [ -z "${AUTHORING_MODEL_OVERRIDE:-}" ]; then
|
|
240
|
+
emit_blocked "This org exposes both Agentforce authoring models, but the selected target could not be classified. Set AUTHORING_MODEL_OVERRIDE=classic or nga after confirming the target metadata."
|
|
241
|
+
fi
|
|
242
|
+
fi
|
|
243
|
+
# AUTHORING_MODEL_OVERRIDE (classic|nga) lets a caller pin the branch when both surfaces coexist.
|
|
244
|
+
case "${AUTHORING_MODEL_OVERRIDE:-}" in
|
|
245
|
+
classic) [ "$HAS_CLASSIC_SURFACE" = "true" ] && AUTHORING_MODEL="classic" ;;
|
|
246
|
+
nga) [ "$HAS_NGA_SURFACE" = "true" ] && AUTHORING_MODEL="nga" ;;
|
|
247
|
+
esac
|
|
248
|
+
|
|
249
|
+
if [ "$AUTHORING_MODEL" = "none" ]; then
|
|
250
|
+
emit_blocked "This org exposes neither the classic Agentforce authoring metadata (GenAiPlugin + GenAiPlannerBundle) nor the next-gen AiAuthoringBundle, so no agent escalation surface can be authored or verified here. Enable Agentforce, then re-run. To provision only the deterministic routing infrastructure (queue, members, QueueRoutingConfig, outbound RoutingFlow) on this org, use the service-omni-* leaf skills directly." \
|
|
251
|
+
"Setup → Einstein / Agentforce → enable Agentforce so the authoring metadata (GenAiPlannerBundle or AiAuthoringBundle) is exposed"
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
# Step 4 - Create-or-adopt the human queue (+ QueueSobject for the context object)
|
|
255
|
+
Q_JSON=$(sf data query --target-org "$ORG" \
|
|
256
|
+
--query "SELECT Id FROM Group WHERE DeveloperName='$QUEUE_DN' AND Type='Queue'" \
|
|
257
|
+
--json 2>/dev/null)
|
|
258
|
+
QUEUE_ID=$(echo "$Q_JSON" | jq -r '.result.records[0].Id // ""')
|
|
259
|
+
|
|
260
|
+
if [ "${PLAN_ONLY:-}" = "1" ]; then
|
|
261
|
+
if [ -n "$QUEUE_ID" ]; then
|
|
262
|
+
emit_plan "Agent '$AGENT_DN' active; queue '$QUEUE_DN' exists. Would ensure the $CONTEXT_OBJECT QueueSobject and at least one active direct-user member (adding from QUEUE_MEMBER_USERNAMES if empty); ONLY if the queue has an eligible member would it then create/adopt+bind the QueueRoutingConfig and deploy outbound flow '$FLOW_DEV_NAME'; finally round-trip escalation config."
|
|
263
|
+
else
|
|
264
|
+
emit_plan "Agent '$AGENT_DN' active; queue '$QUEUE_DN' missing. Would create the queue ($CONTEXT_OBJECT QueueSobject) and ensure at least one active direct-user member (adding from QUEUE_MEMBER_USERNAMES if empty); ONLY if the queue has an eligible member would it then create+bind the QueueRoutingConfig and deploy outbound flow '$FLOW_DEV_NAME'; finally round-trip escalation config."
|
|
265
|
+
fi
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
if [ -z "$QUEUE_ID" ]; then
|
|
269
|
+
# Create the queue + context-object QueueSobject via Metadata API.
|
|
270
|
+
TQ="$TWORK/queue/force-app/main/default/queues"
|
|
271
|
+
mkdir -p "$TQ"
|
|
272
|
+
cat > "$TWORK/queue/sfdx-project.json" <<'JSON'
|
|
273
|
+
{ "packageDirectories": [{ "path": "force-app", "default": true }], "namespace": "", "sourceApiVersion": "67.0" }
|
|
274
|
+
JSON
|
|
275
|
+
# Queue XSD order: doesSendEmailToMembers, name, queueSobject (Metadata API rejects other orders).
|
|
276
|
+
cat > "$TQ/${QUEUE_DN}.queue-meta.xml" <<XML
|
|
277
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
278
|
+
<Queue xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
279
|
+
<doesSendEmailToMembers>false</doesSendEmailToMembers>
|
|
280
|
+
<name>${QUEUE_DN}</name>
|
|
281
|
+
<queueSobject>
|
|
282
|
+
<sobjectType>${CONTEXT_OBJECT}</sobjectType>
|
|
283
|
+
</queueSobject>
|
|
284
|
+
</Queue>
|
|
285
|
+
XML
|
|
286
|
+
if ! DEPLOY_OUT=$(sf project deploy start --target-org "$ORG" --source-dir "$TWORK/queue/force-app" --json 2>/dev/null); then
|
|
287
|
+
emit_blocked "Failed to create queue '$QUEUE_DN'. Raw: $(echo "$DEPLOY_OUT" | head -c 600)"
|
|
288
|
+
fi
|
|
289
|
+
Q_JSON=$(sf data query --target-org "$ORG" \
|
|
290
|
+
--query "SELECT Id FROM Group WHERE DeveloperName='$QUEUE_DN' AND Type='Queue'" --json 2>/dev/null)
|
|
291
|
+
QUEUE_ID=$(echo "$Q_JSON" | jq -r '.result.records[0].Id // ""')
|
|
292
|
+
[ -z "$QUEUE_ID" ] && emit_blocked "Queue '$QUEUE_DN' not found after create attempt."
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
# Ensure the context-object QueueSobject exists; an adopted queue may lack it - bind additively via Data API.
|
|
296
|
+
QS_JSON=$(sf data query --target-org "$ORG" \
|
|
297
|
+
--query "SELECT Id FROM QueueSobject WHERE QueueId='$QUEUE_ID' AND SobjectType='$CONTEXT_OBJECT'" \
|
|
298
|
+
--json 2>/dev/null)
|
|
299
|
+
QUEUE_SOBJECT_PRESENT=false
|
|
300
|
+
[ "$(echo "$QS_JSON" | jq -r '.result.totalSize // 0')" -gt 0 ] && QUEUE_SOBJECT_PRESENT=true
|
|
301
|
+
|
|
302
|
+
if [ "$QUEUE_SOBJECT_PRESENT" != "true" ]; then
|
|
303
|
+
QS_STDERR="$(mktemp)"
|
|
304
|
+
QS_POST=$(sf api request rest --target-org "$ORG" \
|
|
305
|
+
"/services/data/$API_VERSION/sobjects/QueueSobject" \
|
|
306
|
+
--method POST \
|
|
307
|
+
--body "$(jq -n --arg q "$QUEUE_ID" --arg so "$CONTEXT_OBJECT" '{QueueId:$q, SobjectType:$so}')" 2>"$QS_STDERR" || true)
|
|
308
|
+
rm -f "$QS_STDERR"
|
|
309
|
+
if [ "$(echo "$QS_POST" | jq -r '.success // false' 2>/dev/null)" = "true" ]; then
|
|
310
|
+
QUEUE_SOBJECT_PRESENT=true
|
|
311
|
+
else
|
|
312
|
+
QS_ERR=$(echo "$QS_POST" | jq -r '.[0].message // .message // "unknown error"' 2>/dev/null)
|
|
313
|
+
emit_blocked "Queue '$QUEUE_DN' exists but is not bound to $CONTEXT_OBJECT, and the QueueSobject bind failed: $QS_ERR" \
|
|
314
|
+
"Setup → Queues → '$QUEUE_DN' → Supported Objects → add the context object → Save"
|
|
315
|
+
fi
|
|
316
|
+
fi
|
|
317
|
+
|
|
318
|
+
# Step 4b - Ensure the queue has >=1 active direct-user member (Id prefix 005); an empty queue can't
|
|
319
|
+
# receive escalations, so routing (Steps 5/6) is refused without one. Necessary but not sufficient for
|
|
320
|
+
# routability (Omni perms/presence proven only at runtime) - hence the honest evidence key
|
|
321
|
+
# queueHasActiveDirectUserMember. Members come from the pre-validated QUEUE_MEMBER_USERNAMES_CLEAN list.
|
|
322
|
+
count_active_user_members() {
|
|
323
|
+
local gm ids uq
|
|
324
|
+
gm=$(sf data query --target-org "$ORG" \
|
|
325
|
+
--query "SELECT UserOrGroupId FROM GroupMember WHERE GroupId='$QUEUE_ID'" --json 2>/dev/null)
|
|
326
|
+
# Only direct User members (Id prefix 005) can be resolved to IsActive deterministically.
|
|
327
|
+
ids=$(echo "$gm" | jq -r '[.result.records[]?.UserOrGroupId | select(type=="string" and startswith("005"))] | join("\u0027,\u0027")' 2>/dev/null || echo "")
|
|
328
|
+
if [ -z "$ids" ]; then echo 0; return; fi
|
|
329
|
+
uq=$(sf data query --target-org "$ORG" \
|
|
330
|
+
--query "SELECT COUNT(Id) c FROM User WHERE IsActive=true AND Id IN ('$ids')" --json 2>/dev/null)
|
|
331
|
+
echo "$uq" | jq -r '.result.records[0].c // (.result.totalSize // 0)' 2>/dev/null || echo 0
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
MEMBER_COUNT=$(count_active_user_members)
|
|
335
|
+
[ -z "$MEMBER_COUNT" ] && MEMBER_COUNT=0
|
|
336
|
+
|
|
337
|
+
if [ "$MEMBER_COUNT" -eq 0 ] && [ -n "$QUEUE_MEMBER_USERNAMES_CLEAN" ]; then
|
|
338
|
+
IFS=',' read -ra _UNAMES <<< "$QUEUE_MEMBER_USERNAMES_CLEAN"
|
|
339
|
+
for uname in "${_UNAMES[@]}"; do
|
|
340
|
+
[ -z "$uname" ] && continue
|
|
341
|
+
U_JSON=$(sf data query --target-org "$ORG" \
|
|
342
|
+
--query "SELECT Id FROM User WHERE Username='$uname' AND IsActive=true LIMIT 1" --json 2>/dev/null)
|
|
343
|
+
U_ID=$(echo "$U_JSON" | jq -r '.result.records[0].Id // ""')
|
|
344
|
+
if [ -z "$U_ID" ]; then
|
|
345
|
+
emit_blocked "QUEUE_MEMBER_USERNAMES user '$uname' was not found as an active User on '$ORG'. Create or reactivate the user, or correct the username, then rerun."
|
|
346
|
+
fi
|
|
347
|
+
GM_STDERR="$(mktemp)"
|
|
348
|
+
GM_POST=$(sf api request rest --target-org "$ORG" \
|
|
349
|
+
"/services/data/$API_VERSION/sobjects/GroupMember" \
|
|
350
|
+
--method POST \
|
|
351
|
+
--body "$(jq -n --arg g "$QUEUE_ID" --arg u "$U_ID" '{GroupId:$g, UserOrGroupId:$u}')" 2>"$GM_STDERR" || true)
|
|
352
|
+
rm -f "$GM_STDERR"
|
|
353
|
+
if [ "$(echo "$GM_POST" | jq -r '.success // false' 2>/dev/null)" != "true" ]; then
|
|
354
|
+
# A duplicate membership (already a member) is benign; anything else is fatal.
|
|
355
|
+
if ! echo "$GM_POST" | jq -e '((.[0].errorCode // "") | test("DUPLICATE"))' >/dev/null 2>&1; then
|
|
356
|
+
GM_ERR=$(echo "$GM_POST" | jq -r '.[0].message // .message // "unknown error"' 2>/dev/null)
|
|
357
|
+
emit_blocked "Failed to add '$uname' to queue '$QUEUE_DN' as a member: $GM_ERR"
|
|
358
|
+
fi
|
|
359
|
+
fi
|
|
360
|
+
done
|
|
361
|
+
MEMBER_COUNT=$(count_active_user_members)
|
|
362
|
+
[ -z "$MEMBER_COUNT" ] && MEMBER_COUNT=0
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
QUEUE_HAS_ACTIVE_DIRECT_USER_MEMBER=false
|
|
366
|
+
[ "$MEMBER_COUNT" -gt 0 ] && QUEUE_HAS_ACTIVE_DIRECT_USER_MEMBER=true
|
|
367
|
+
|
|
368
|
+
# Membership gate: Steps 5 (QRC bind) + 6 (Flow) run only with an eligible member; else left
|
|
369
|
+
# unconfigured and the verdict is INCOMPLETE (never a flow routing to nobody).
|
|
370
|
+
QUEUE_ROUTING_CONFIG_PRESENT=false
|
|
371
|
+
QUEUE_ROUTING_CONFIG_BOUND=false
|
|
372
|
+
RC_ID=""
|
|
373
|
+
FLOW_ACTIVE_ID=""
|
|
374
|
+
if [ "$QUEUE_HAS_ACTIVE_DIRECT_USER_MEMBER" = "true" ]; then
|
|
375
|
+
|
|
376
|
+
# Step 5 - Ensure a QueueRoutingConfig, then bind it to the queue (Group.QueueRoutingConfigId)
|
|
377
|
+
RC_DN="${QUEUE_DN}_RC"
|
|
378
|
+
RC_JSON=$(sf data query --target-org "$ORG" \
|
|
379
|
+
--query "SELECT Id FROM QueueRoutingConfig WHERE DeveloperName='$RC_DN'" --json 2>/dev/null)
|
|
380
|
+
RC_ID=$(echo "$RC_JSON" | jq -r '.result.records[0].Id // ""')
|
|
381
|
+
|
|
382
|
+
if [ -z "$RC_ID" ]; then
|
|
383
|
+
# Create QRC via Data API. RoutingPriority is required (REQUIRED_FIELD_MISSING if omitted).
|
|
384
|
+
RC_STDERR="$(mktemp)"
|
|
385
|
+
RC_POST=$(sf api request rest --target-org "$ORG" \
|
|
386
|
+
"/services/data/$API_VERSION/sobjects/QueueRoutingConfig" \
|
|
387
|
+
--method POST \
|
|
388
|
+
--body "$(jq -n --arg dn "$RC_DN" --arg ml "${QUEUE_DN} Routing Config" \
|
|
389
|
+
'{DeveloperName:$dn, MasterLabel:$ml, RoutingModel:"MostAvailable", CapacityWeight:5, RoutingPriority:1, IsAttributeBased:false}')" 2>"$RC_STDERR" || true)
|
|
390
|
+
rm -f "$RC_STDERR"
|
|
391
|
+
if [ "$(echo "$RC_POST" | jq -r '.success // false' 2>/dev/null)" = "true" ]; then
|
|
392
|
+
RC_ID=$(echo "$RC_POST" | jq -r '.id')
|
|
393
|
+
else
|
|
394
|
+
if echo "$RC_POST" | jq -e '(.name // "") == "INVALID_TYPE" or ((.[0].errorCode // "") == "INVALID_TYPE")' >/dev/null 2>&1; then
|
|
395
|
+
emit_blocked "QueueRoutingConfig is not available because Omni-Channel is not enabled on this org. Enable Omni-Channel in Setup, then rerun."
|
|
396
|
+
fi
|
|
397
|
+
RC_ERR=$(echo "$RC_POST" | jq -r '.[0].message // .message // "unknown error"' 2>/dev/null)
|
|
398
|
+
emit_blocked "Failed to create QueueRoutingConfig '$RC_DN': $RC_ERR"
|
|
399
|
+
fi
|
|
400
|
+
fi
|
|
401
|
+
QUEUE_ROUTING_CONFIG_PRESENT=false
|
|
402
|
+
[ -n "$RC_ID" ] && QUEUE_ROUTING_CONFIG_PRESENT=true
|
|
403
|
+
|
|
404
|
+
# Bind the QRC to the queue (Group.QueueRoutingConfigId) if not already linked.
|
|
405
|
+
QUEUE_ROUTING_CONFIG_BOUND=false
|
|
406
|
+
if [ -n "$RC_ID" ]; then
|
|
407
|
+
G_JSON=$(sf data query --target-org "$ORG" \
|
|
408
|
+
--query "SELECT Id, QueueRoutingConfigId FROM Group WHERE Id='$QUEUE_ID'" --json 2>/dev/null)
|
|
409
|
+
CUR_RC=$(echo "$G_JSON" | jq -r '.result.records[0].QueueRoutingConfigId // ""')
|
|
410
|
+
if [ "$CUR_RC" = "$RC_ID" ]; then
|
|
411
|
+
QUEUE_ROUTING_CONFIG_BOUND=true
|
|
412
|
+
else
|
|
413
|
+
G_STDERR="$(mktemp)"
|
|
414
|
+
sf api request rest --target-org "$ORG" \
|
|
415
|
+
"/services/data/$API_VERSION/sobjects/Group/$QUEUE_ID" \
|
|
416
|
+
--method PATCH \
|
|
417
|
+
--body "$(jq -n --arg rc "$RC_ID" '{QueueRoutingConfigId:$rc}')" >/dev/null 2>"$G_STDERR" || true
|
|
418
|
+
G_ERR=$(head -c 400 "$G_STDERR" 2>/dev/null || true)
|
|
419
|
+
rm -f "$G_STDERR"
|
|
420
|
+
# PATCH returns 204 (empty body) on success - re-query to CONFIRM the binding persisted.
|
|
421
|
+
G_JSON=$(sf data query --target-org "$ORG" \
|
|
422
|
+
--query "SELECT Id, QueueRoutingConfigId FROM Group WHERE Id='$QUEUE_ID'" --json 2>/dev/null)
|
|
423
|
+
[ "$(echo "$G_JSON" | jq -r '.result.records[0].QueueRoutingConfigId // ""')" = "$RC_ID" ] && QUEUE_ROUTING_CONFIG_BOUND=true
|
|
424
|
+
if [ "$QUEUE_ROUTING_CONFIG_BOUND" != "true" ]; then
|
|
425
|
+
emit_blocked "Created/adopted QueueRoutingConfig '$RC_DN' but failed to bind it to queue '$QUEUE_DN' (Group.QueueRoutingConfigId did not persist).${G_ERR:+ Raw: $G_ERR}"
|
|
426
|
+
fi
|
|
427
|
+
fi
|
|
428
|
+
fi
|
|
429
|
+
|
|
430
|
+
# Step 6 - Deploy the bundled outbound QueueBased RoutingFlow (tokens substituted)
|
|
431
|
+
FLOW_JSON=$(sf data query --target-org "$ORG" \
|
|
432
|
+
--query "SELECT ApiName, ActiveVersionId FROM FlowDefinitionView WHERE ApiName='$FLOW_DEV_NAME' AND ProcessType='RoutingFlow'" \
|
|
433
|
+
--json 2>/dev/null)
|
|
434
|
+
FLOW_ACTIVE_ID=$(echo "$FLOW_JSON" | jq -r '.result.records[0].ActiveVersionId // ""')
|
|
435
|
+
|
|
436
|
+
if [ -z "$FLOW_ACTIVE_ID" ]; then
|
|
437
|
+
[ -f "$ASSET_FLOW" ] || emit_blocked "Bundled outbound flow asset not found at $ASSET_FLOW"
|
|
438
|
+
TF="$TWORK/flow/force-app/main/default/flows"
|
|
439
|
+
mkdir -p "$TF"
|
|
440
|
+
cat > "$TWORK/flow/sfdx-project.json" <<'JSON'
|
|
441
|
+
{ "packageDirectories": [{ "path": "force-app", "default": true }], "namespace": "", "sourceApiVersion": "67.0" }
|
|
442
|
+
JSON
|
|
443
|
+
SC_LABEL_SED="$(sed_repl_escape "$(xml_escape "$SERVICE_CHANNEL_LABEL")")"
|
|
444
|
+
FLOW_LABEL_SED="$(sed_repl_escape "$(xml_escape "$FLOW_LABEL")")"
|
|
445
|
+
sed -e "s|__QUEUE_DEVELOPER_NAME__|${QUEUE_DN}|g" \
|
|
446
|
+
-e "s|__SERVICE_CHANNEL_DEV_NAME__|${SERVICE_CHANNEL_DEV_NAME}|g" \
|
|
447
|
+
-e "s|__SERVICE_CHANNEL_LABEL__|${SC_LABEL_SED}|g" \
|
|
448
|
+
-e "s|__FLOW_DEVELOPER_NAME__|${FLOW_DEV_NAME}|g" \
|
|
449
|
+
-e "s|__FLOW_LABEL__|${FLOW_LABEL_SED}|g" \
|
|
450
|
+
"$ASSET_FLOW" > "$TF/${FLOW_DEV_NAME}.flow-meta.xml"
|
|
451
|
+
# Deploy from inside the temp project so sf uses its sourceApiVersion (67.0), not an ancestor's.
|
|
452
|
+
if ! DEPLOY_OUT=$(cd "$TWORK/flow" && sf project deploy start --target-org "$ORG" --source-dir force-app --json 2>/dev/null); then
|
|
453
|
+
emit_blocked "Failed to deploy outbound flow '$FLOW_DEV_NAME'. Raw: $(echo "$DEPLOY_OUT" | head -c 600)"
|
|
454
|
+
fi
|
|
455
|
+
FLOW_JSON=$(sf data query --target-org "$ORG" \
|
|
456
|
+
--query "SELECT ApiName, ActiveVersionId FROM FlowDefinitionView WHERE ApiName='$FLOW_DEV_NAME' AND ProcessType='RoutingFlow'" \
|
|
457
|
+
--json 2>/dev/null)
|
|
458
|
+
FLOW_ACTIVE_ID=$(echo "$FLOW_JSON" | jq -r '.result.records[0].ActiveVersionId // ""')
|
|
459
|
+
fi
|
|
460
|
+
|
|
461
|
+
fi # end membership gate - QRC bind + Flow activation skipped when the queue has no eligible member
|
|
462
|
+
|
|
463
|
+
# Step 6b - Optional deterministic authoring of the doc-driven surfaces (AUTHOR_SURFACES=1). Default is
|
|
464
|
+
# verify-only; with the topic/bundle names supplied this authors canEscalate + outboundRouteConfigs
|
|
465
|
+
# (retrieve → patch → deploy → reactivate). Runs only after the membership gate. See references/.
|
|
466
|
+
if [ "${AUTHOR_SURFACES:-0}" = "1" ] && [ "$QUEUE_HAS_ACTIVE_DIRECT_USER_MEMBER" = "true" ] && [ "$AUTHORING_MODEL" = "classic" ]; then
|
|
467
|
+
if [ -z "$ESCALATION_TOPIC" ] && [ -z "$PLANNER_BUNDLE" ]; then
|
|
468
|
+
emit_blocked "AUTHOR_SURFACES=1 requires escalation_topic_api_name and/or planner_bundle_api_name so the skill knows which topic/bundle to author. Re-run with those identifiers or omit AUTHOR_SURFACES."
|
|
469
|
+
fi
|
|
470
|
+
|
|
471
|
+
# Authoring lifecycle: publish → retrieve (metadata --unzip) → deactivate → XSD-ordered patch →
|
|
472
|
+
# deploy (--metadata-dir) → activate (fail closed) → re-read latest BotVersion. See references/classifier-contracts.md.
|
|
473
|
+
|
|
474
|
+
# 1) Publish so the compiled planner bundle exists/refreshed.
|
|
475
|
+
if ! sf agent publish authoring-bundle --api-name "$AGENT_DN" --target-org "$ORG" --json >/dev/null 2>&1; then
|
|
476
|
+
emit_blocked "AUTHOR_SURFACES: 'sf agent publish authoring-bundle' failed for '$AGENT_DN'; cannot author escalation surfaces."
|
|
477
|
+
fi
|
|
478
|
+
|
|
479
|
+
# 2) Retrieve in metadata format + --unzip (else --target-metadata-dir yields a ZIP); find the XML + package.xml root.
|
|
480
|
+
APLUGIN=""; ABUNDLE=""; AROOT_PLUGIN=""; AROOT_BUNDLE=""
|
|
481
|
+
if [ -n "$ESCALATION_TOPIC" ]; then
|
|
482
|
+
ARET="$TWORK/author-plugin"; mkdir -p "$ARET"
|
|
483
|
+
if ! sf project retrieve start --target-org "$ORG" --metadata "GenAiPlugin:$ESCALATION_TOPIC" \
|
|
484
|
+
--target-metadata-dir "$ARET" --unzip >/dev/null 2>&1; then
|
|
485
|
+
emit_blocked "AUTHOR_SURFACES: failed to retrieve GenAiPlugin '$ESCALATION_TOPIC' (metadata format)."
|
|
486
|
+
fi
|
|
487
|
+
APLUGIN=$(find "$ARET" -type f \( -name '*.genAiPlugin' -o -name '*.genAiPlugin-meta.xml' \) 2>/dev/null | head -1)
|
|
488
|
+
AROOT_PLUGIN=$(dirname "$(find "$ARET" -type f -name package.xml 2>/dev/null | head -1)" 2>/dev/null || echo "")
|
|
489
|
+
if [ -z "$APLUGIN" ] || [ -z "$AROOT_PLUGIN" ] || [ ! -d "$AROOT_PLUGIN" ]; then
|
|
490
|
+
emit_blocked "AUTHOR_SURFACES: retrieved GenAiPlugin '$ESCALATION_TOPIC' but found no metadata-format file/package.xml to patch."
|
|
491
|
+
fi
|
|
492
|
+
fi
|
|
493
|
+
if [ -n "$PLANNER_BUNDLE" ]; then
|
|
494
|
+
ARETB="$TWORK/author-bundle"; mkdir -p "$ARETB"
|
|
495
|
+
if ! sf project retrieve start --target-org "$ORG" --metadata "GenAiPlannerBundle:$PLANNER_BUNDLE" \
|
|
496
|
+
--target-metadata-dir "$ARETB" --unzip >/dev/null 2>&1; then
|
|
497
|
+
emit_blocked "AUTHOR_SURFACES: failed to retrieve GenAiPlannerBundle '$PLANNER_BUNDLE' (metadata format)."
|
|
498
|
+
fi
|
|
499
|
+
ABUNDLE=$(find "$ARETB" -type f \( -name '*.genAiPlannerBundle' -o -name '*.genAiPlannerBundle-meta.xml' \) 2>/dev/null | head -1)
|
|
500
|
+
AROOT_BUNDLE=$(dirname "$(find "$ARETB" -type f -name package.xml 2>/dev/null | head -1)" 2>/dev/null || echo "")
|
|
501
|
+
if [ -z "$ABUNDLE" ] || [ -z "$AROOT_BUNDLE" ] || [ ! -d "$AROOT_BUNDLE" ]; then
|
|
502
|
+
emit_blocked "AUTHOR_SURFACES: retrieved GenAiPlannerBundle '$PLANNER_BUNDLE' but found no metadata-format file/package.xml to patch."
|
|
503
|
+
fi
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
# 3) Deactivate (deploys fail while active).
|
|
507
|
+
if ! { echo "Y" | sf agent deactivate --api-name "$AGENT_DN" --target-org "$ORG" --json >/dev/null 2>&1; }; then
|
|
508
|
+
emit_blocked "AUTHOR_SURFACES: 'sf agent deactivate' failed for '$AGENT_DN'; refusing to deploy while the agent is active."
|
|
509
|
+
fi
|
|
510
|
+
AGENT_ACTIVE=false
|
|
511
|
+
|
|
512
|
+
# 4) Patch (XSD-ordered) + deploy (--metadata-dir); on failure, best-effort reactivate then block.
|
|
513
|
+
if [ -n "$APLUGIN" ]; then
|
|
514
|
+
if ! node "$SCRIPT_DIR/patch-escalation-surfaces.mjs" canEscalate "$APLUGIN" >/dev/null 2>&1; then
|
|
515
|
+
echo "Y" | sf agent activate --api-name "$AGENT_DN" --target-org "$ORG" --json >/dev/null 2>&1 || true
|
|
516
|
+
emit_blocked "AUTHOR_SURFACES: could not safely set canEscalate=true on GenAiPlugin '$ESCALATION_TOPIC' (no safe XSD-ordered insertion point)."
|
|
517
|
+
fi
|
|
518
|
+
if ! DEPLOY_OUT=$(sf project deploy start --target-org "$ORG" --metadata-dir "$AROOT_PLUGIN" --json 2>/dev/null); then
|
|
519
|
+
echo "Y" | sf agent activate --api-name "$AGENT_DN" --target-org "$ORG" --json >/dev/null 2>&1 || true
|
|
520
|
+
emit_blocked "AUTHOR_SURFACES: failed to deploy canEscalate patch for '$ESCALATION_TOPIC'. Raw: $(echo "$DEPLOY_OUT" | head -c 400)"
|
|
521
|
+
fi
|
|
522
|
+
fi
|
|
523
|
+
if [ -n "$ABUNDLE" ]; then
|
|
524
|
+
if ! node "$SCRIPT_DIR/patch-escalation-surfaces.mjs" outboundRoute "$ABUNDLE" "$FLOW_DEV_NAME" >/dev/null 2>&1; then
|
|
525
|
+
echo "Y" | sf agent activate --api-name "$AGENT_DN" --target-org "$ORG" --json >/dev/null 2>&1 || true
|
|
526
|
+
emit_blocked "AUTHOR_SURFACES: could not safely wire outboundRouteConfigs on a Messaging surface of '$PLANNER_BUNDLE' (no Messaging-class planner surface / <surface> anchor found)."
|
|
527
|
+
fi
|
|
528
|
+
if ! DEPLOY_OUT=$(sf project deploy start --target-org "$ORG" --metadata-dir "$AROOT_BUNDLE" --json 2>/dev/null); then
|
|
529
|
+
echo "Y" | sf agent activate --api-name "$AGENT_DN" --target-org "$ORG" --json >/dev/null 2>&1 || true
|
|
530
|
+
emit_blocked "AUTHOR_SURFACES: failed to deploy outboundRouteConfigs patch for '$PLANNER_BUNDLE'. Raw: $(echo "$DEPLOY_OUT" | head -c 400)"
|
|
531
|
+
fi
|
|
532
|
+
fi
|
|
533
|
+
|
|
534
|
+
# 5) Reactivate - FAIL CLOSED (never swallow with || true; an inactive agent cannot serve).
|
|
535
|
+
if ! { echo "Y" | sf agent activate --api-name "$AGENT_DN" --target-org "$ORG" --json >/dev/null 2>&1; }; then
|
|
536
|
+
emit_blocked "AUTHOR_SURFACES: 'sf agent activate' failed for '$AGENT_DN' after deploying the escalation surfaces; the agent is left INACTIVE." \
|
|
537
|
+
"Setup → Agentforce → Agents → '$AGENT_DN' → activate the latest version"
|
|
538
|
+
fi
|
|
539
|
+
|
|
540
|
+
# 6) Re-verify the LATEST BotVersion is Active (never trust the pre-authoring AGENT_ACTIVE value).
|
|
541
|
+
BOT_JSON_AFTER=$(sf data query --target-org "$ORG" \
|
|
542
|
+
--query "SELECT Id,DeveloperName,MasterLabel,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE DeveloperName='$AGENT_DN'" \
|
|
543
|
+
--json 2>/dev/null)
|
|
544
|
+
echo "$BOT_JSON_AFTER" > "$TWORK/bot-after.json"
|
|
545
|
+
AGENT_VERDICT_AFTER=$(node "$SCRIPT_DIR/classify-agent-active.mjs" "$TWORK/bot-after.json" "$AGENT_DN" 2>/dev/null || echo '{"ready":false}')
|
|
546
|
+
if [ "$(echo "$AGENT_VERDICT_AFTER" | jq -r '.ready')" = "true" ]; then
|
|
547
|
+
AGENT_ACTIVE=true
|
|
548
|
+
else
|
|
549
|
+
AGENT_ACTIVE=false
|
|
550
|
+
emit_blocked "AUTHOR_SURFACES: after authoring + reactivation the latest BotVersion for '$AGENT_DN' is not Active; refusing to report CONFIGURED against a stale/inactive version." \
|
|
551
|
+
"Setup → Agentforce → Agents → '$AGENT_DN' → activate the latest version"
|
|
552
|
+
fi
|
|
553
|
+
fi
|
|
554
|
+
|
|
555
|
+
# Step 7 - Verify doc-driven surfaces when their identifiers were provided
|
|
556
|
+
CAN_ESCALATE=false
|
|
557
|
+
if [ -n "$ESCALATION_TOPIC" ]; then
|
|
558
|
+
RET="$TWORK/plugin"
|
|
559
|
+
mkdir -p "$RET"
|
|
560
|
+
# --unzip: without it --target-metadata-dir yields a ZIP and the grep below scans binary, so a
|
|
561
|
+
# correctly configured topic is misreported as incomplete (mirrors the authoring path at Step 6).
|
|
562
|
+
if sf project retrieve start --target-org "$ORG" --metadata "GenAiPlugin:$ESCALATION_TOPIC" \
|
|
563
|
+
--target-metadata-dir "$RET" --unzip >/dev/null 2>&1; then
|
|
564
|
+
if grep -Rql "<canEscalate>true</canEscalate>" "$RET" 2>/dev/null; then
|
|
565
|
+
CAN_ESCALATE=true
|
|
566
|
+
fi
|
|
567
|
+
fi
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
OUTBOUND_ROUTE_NAME=""
|
|
571
|
+
OUTBOUND_ROUTE_TYPE=""
|
|
572
|
+
OUTBOUND_ROUTE_SURFACE=""
|
|
573
|
+
OUTBOUND_ROUTE_SAME_BLOCK=false
|
|
574
|
+
OUTBOUND_ROUTE_MESSAGING_SURFACE=false
|
|
575
|
+
if [ -n "$PLANNER_BUNDLE" ]; then
|
|
576
|
+
RETB="$TWORK/bundle"
|
|
577
|
+
mkdir -p "$RETB"
|
|
578
|
+
# --unzip: else --target-metadata-dir yields a ZIP and the find/cat below reads binary, so a wired
|
|
579
|
+
# outbound route is misreported as missing.
|
|
580
|
+
if sf project retrieve start --target-org "$ORG" --metadata "GenAiPlannerBundle:$PLANNER_BUNDLE" \
|
|
581
|
+
--target-metadata-dir "$RETB" --unzip >/dev/null 2>&1; then
|
|
582
|
+
# Couple outboundRouteName + outboundRouteType within the same planner-surface block.
|
|
583
|
+
BUNDLE_CAT="$TWORK/bundle-cat.xml"
|
|
584
|
+
find "$RETB" -type f -name '*.xml' -exec cat {} \; > "$BUNDLE_CAT" 2>/dev/null || true
|
|
585
|
+
ROUTE_JSON=$(node "$SCRIPT_DIR/extract-outbound-route.mjs" "$BUNDLE_CAT" "$FLOW_DEV_NAME" 2>/dev/null || echo '{}')
|
|
586
|
+
OUTBOUND_ROUTE_NAME=$(echo "$ROUTE_JSON" | jq -r '.name // ""')
|
|
587
|
+
OUTBOUND_ROUTE_TYPE=$(echo "$ROUTE_JSON" | jq -r '.type // ""')
|
|
588
|
+
OUTBOUND_ROUTE_SURFACE=$(echo "$ROUTE_JSON" | jq -r '.surface // ""')
|
|
589
|
+
OUTBOUND_ROUTE_SAME_BLOCK=$(echo "$ROUTE_JSON" | jq -r '.sameBlock // false')
|
|
590
|
+
OUTBOUND_ROUTE_MESSAGING_SURFACE=$(echo "$ROUTE_JSON" | jq -r '.messagingSurface // false')
|
|
591
|
+
fi
|
|
592
|
+
fi
|
|
593
|
+
|
|
594
|
+
# Step 7b (NGA) - Verify the next-gen escalation surface by retrieving the agent's AiAuthoringBundle and
|
|
595
|
+
# scanning its Agent Script: a Service agent needs a reachable @utils.escalate (with connection messaging);
|
|
596
|
+
# an Employee agent (no messaging) needs a create-record action. Authoring the surface itself is delegated
|
|
597
|
+
# to agentforce-generate (see next_steps); this skill verifies + reports. Details: references/nga-escalation.md.
|
|
598
|
+
NGA_SURFACE_PRESENT=false
|
|
599
|
+
NGA_AGENT_TYPE="unknown"
|
|
600
|
+
if [ "$AUTHORING_MODEL" = "nga" ]; then
|
|
601
|
+
NRET="$TWORK/nga-bundle"
|
|
602
|
+
mkdir -p "$NRET"
|
|
603
|
+
# --unzip: else --target-metadata-dir yields a ZIP and the NGA classifier scans binary, so a present
|
|
604
|
+
# escalation surface is misreported as missing.
|
|
605
|
+
if sf project retrieve start --target-org "$ORG" --metadata "AiAuthoringBundle:$AGENT_DN" \
|
|
606
|
+
--target-metadata-dir "$NRET" --unzip >/dev/null 2>&1; then
|
|
607
|
+
NGA_JSON=$(node "$SCRIPT_DIR/classify-nga-escalation.mjs" "$NRET" 2>/dev/null || echo '{}')
|
|
608
|
+
NGA_SURFACE_PRESENT=$(echo "$NGA_JSON" | jq -r '.escalationSurfacePresent // false')
|
|
609
|
+
NGA_AGENT_TYPE=$(echo "$NGA_JSON" | jq -r '.agentType // "unknown"')
|
|
610
|
+
fi
|
|
611
|
+
fi
|
|
612
|
+
|
|
613
|
+
THRESHOLD_FLAG=false
|
|
614
|
+
[ "$THRESHOLD_AUTHORED" = "1" ] && THRESHOLD_FLAG=true
|
|
615
|
+
|
|
616
|
+
# Render the numeric threshold into the directive template (echoed in the verdict as evidence).
|
|
617
|
+
THRESHOLD_TEMPLATE="$SCRIPT_DIR/../assets/escalation-thresholds.instructions.md"
|
|
618
|
+
DIRECTIVE_RENDERED=false
|
|
619
|
+
if [ -f "$THRESHOLD_TEMPLATE" ]; then
|
|
620
|
+
RENDERED_DIRECTIVE="$TWORK/escalation-thresholds.rendered.md"
|
|
621
|
+
sed "s/__DEFAULT_FAILURES__/${DEFAULT_FAILURE_THRESHOLD}/g" "$THRESHOLD_TEMPLATE" > "$RENDERED_DIRECTIVE" 2>/dev/null \
|
|
622
|
+
&& DIRECTIVE_RENDERED=true
|
|
623
|
+
fi
|
|
624
|
+
|
|
625
|
+
# Step 8 - Assemble evidence + render the deterministic verdict
|
|
626
|
+
EVID="$TWORK/evidence.json"
|
|
627
|
+
jq -n \
|
|
628
|
+
--argjson canEscalate "$CAN_ESCALATE" \
|
|
629
|
+
--arg outboundRouteName "$OUTBOUND_ROUTE_NAME" \
|
|
630
|
+
--arg outboundRouteType "$OUTBOUND_ROUTE_TYPE" \
|
|
631
|
+
--arg outboundRouteSurface "$OUTBOUND_ROUTE_SURFACE" \
|
|
632
|
+
--argjson outboundRouteSameBlock "$OUTBOUND_ROUTE_SAME_BLOCK" \
|
|
633
|
+
--argjson outboundRouteMessagingSurface "$OUTBOUND_ROUTE_MESSAGING_SURFACE" \
|
|
634
|
+
--arg expectedFlowName "$FLOW_DEV_NAME" \
|
|
635
|
+
--arg flowActiveVersionId "$FLOW_ACTIVE_ID" \
|
|
636
|
+
--arg queueId "$QUEUE_ID" \
|
|
637
|
+
--argjson queueSobjectPresent "$QUEUE_SOBJECT_PRESENT" \
|
|
638
|
+
--argjson queueHasActiveDirectUserMember "$QUEUE_HAS_ACTIVE_DIRECT_USER_MEMBER" \
|
|
639
|
+
--argjson queueMemberCount "$MEMBER_COUNT" \
|
|
640
|
+
--argjson queueRoutingConfigPresent "$QUEUE_ROUTING_CONFIG_PRESENT" \
|
|
641
|
+
--argjson queueRoutingConfigBound "$QUEUE_ROUTING_CONFIG_BOUND" \
|
|
642
|
+
--argjson agentActive "$AGENT_ACTIVE" \
|
|
643
|
+
--argjson thresholdAuthored "$THRESHOLD_FLAG" \
|
|
644
|
+
--argjson defaultFailureThreshold "$DEFAULT_FAILURE_THRESHOLD" \
|
|
645
|
+
--arg authoringModel "$AUTHORING_MODEL" \
|
|
646
|
+
--argjson ngaEscalationSurfacePresent "$NGA_SURFACE_PRESENT" \
|
|
647
|
+
--arg ngaAgentType "$NGA_AGENT_TYPE" \
|
|
648
|
+
'{canEscalate:$canEscalate, outboundRouteName:$outboundRouteName, outboundRouteType:$outboundRouteType,
|
|
649
|
+
outboundRouteSurface:$outboundRouteSurface, outboundRouteSameBlock:$outboundRouteSameBlock,
|
|
650
|
+
outboundRouteMessagingSurface:$outboundRouteMessagingSurface, expectedFlowName:$expectedFlowName,
|
|
651
|
+
flowActiveVersionId:$flowActiveVersionId, queueId:$queueId, queueSobjectPresent:$queueSobjectPresent,
|
|
652
|
+
queueHasActiveDirectUserMember:$queueHasActiveDirectUserMember, queueMemberCount:$queueMemberCount,
|
|
653
|
+
queueRoutingConfigPresent:$queueRoutingConfigPresent, queueRoutingConfigBound:$queueRoutingConfigBound,
|
|
654
|
+
agentActive:$agentActive, thresholdAuthored:$thresholdAuthored,
|
|
655
|
+
defaultFailureThreshold:$defaultFailureThreshold, authoringModel:$authoringModel,
|
|
656
|
+
ngaEscalationSurfacePresent:$ngaEscalationSurfacePresent, ngaAgentType:$ngaAgentType}' > "$EVID"
|
|
657
|
+
|
|
658
|
+
CONFIG_VERDICT=$(node "$SCRIPT_DIR/verify-escalation-config.mjs" "$EVID")
|
|
659
|
+
# Status mirrors the deterministic three-state verdict: CONFIGURED (routing + surface + policy),
|
|
660
|
+
# ROUTING_CONFIGURED_POLICY_PENDING (routing/surface done but failure-threshold directive missing),
|
|
661
|
+
# or INCOMPLETE. A missing policy can no longer masquerade as fully CONFIGURED.
|
|
662
|
+
STATUS=$(echo "$CONFIG_VERDICT" | jq -r '.verdict')
|
|
663
|
+
case "$STATUS" in
|
|
664
|
+
CONFIGURED|ROUTING_CONFIGURED_POLICY_PENDING|INCOMPLETE) ;;
|
|
665
|
+
*) STATUS="INCOMPLETE" ;;
|
|
666
|
+
esac
|
|
667
|
+
|
|
668
|
+
jq -n \
|
|
669
|
+
--arg skill "$SKILL_NAME" \
|
|
670
|
+
--arg status "$STATUS" \
|
|
671
|
+
--arg agent "$AGENT_DN" \
|
|
672
|
+
--arg queue "$QUEUE_DN" \
|
|
673
|
+
--arg qid "$QUEUE_ID" \
|
|
674
|
+
--argjson qmembers "$MEMBER_COUNT" \
|
|
675
|
+
--arg flow "$FLOW_DEV_NAME" \
|
|
676
|
+
--arg flowActive "$FLOW_ACTIVE_ID" \
|
|
677
|
+
--arg topic "$ESCALATION_TOPIC" \
|
|
678
|
+
--arg bundle "$PLANNER_BUNDLE" \
|
|
679
|
+
--argjson config "$CONFIG_VERDICT" \
|
|
680
|
+
--argjson thresholdValue "$DEFAULT_FAILURE_THRESHOLD" \
|
|
681
|
+
--argjson thresholdAuthored "$THRESHOLD_FLAG" \
|
|
682
|
+
--argjson directiveRendered "$DIRECTIVE_RENDERED" \
|
|
683
|
+
--arg authoringModel "$AUTHORING_MODEL" \
|
|
684
|
+
--argjson ngaSurface "$NGA_SURFACE_PRESENT" \
|
|
685
|
+
--arg ngaAgentType "$NGA_AGENT_TYPE" \
|
|
686
|
+
'{
|
|
687
|
+
skill: $skill,
|
|
688
|
+
status: $status,
|
|
689
|
+
authoring_model: $authoringModel,
|
|
690
|
+
agent: ({developer_name:$agent, active:true} + (if $authoringModel=="nga" then {type:$ngaAgentType, escalation_surface_present:$ngaSurface} else {} end)),
|
|
691
|
+
queue: {developer_name:$queue, id:$qid, active_direct_user_member_count:$qmembers},
|
|
692
|
+
outbound_flow: {api_name:$flow, active_version_id:(if $flowActive=="" then null else $flowActive end)},
|
|
693
|
+
escalation_topic: (if $topic=="" then null else $topic end),
|
|
694
|
+
planner_bundle: (if $bundle=="" then null else $bundle end),
|
|
695
|
+
threshold: {default_failures:$thresholdValue, authored:$thresholdAuthored, directive_rendered:$directiveRendered},
|
|
696
|
+
config_verification: $config,
|
|
697
|
+
next_steps: (if $status=="CONFIGURED" then []
|
|
698
|
+
elif $status=="ROUTING_CONFIGURED_POLICY_PENDING" then
|
|
699
|
+
(["Routing and the agent escalation surface are verified, but the failure-threshold escalation policy (default two-failure / password-reset override) is not yet authored or confirmed. Author the threshold directive (AUTHOR_SURFACES=1) or confirm it in the topic instructions, then re-run to reach CONFIGURED. See references/failure-policy.md."])
|
|
700
|
+
elif $authoringModel=="nga" then
|
|
701
|
+
(["Deterministic routing infra is verified. Author the NGA escalation surface with agentforce-generate: for a Service agent add a reachable {!@utils.escalate} action (needs connection messaging:); for an Employee agent add a create-record (Case/Incident) action that Omni-routes to the queue. See references/nga-escalation.md, then re-run to reach CONFIGURED."]) else
|
|
702
|
+
(["Author the doc-driven surfaces listed in config_verification.missing (see references/escalation-mechanism.md and failure-policy.md), then re-run to reach CONFIGURED."]) end),
|
|
703
|
+
manual_actions: [],
|
|
704
|
+
blocking_issue: null
|
|
705
|
+
}'
|