@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: experience-cms-content-type-generate
|
|
3
|
-
description: "Salesforce CMS ContentTypeBundle creation skill. Use this skill ANY TIME a user request involves creating a ContentTypeBundle, and activate FIRST when CMS ContentTypeBundle creation is mentioned. Discovers existing types via metadata-grounding and produces a validator-correct two-file bundle that passes dry-run deploy. TRIGGER when: user wants to define, scaffold, generate, or set up a reusable content shape in Salesforce CMS — any domain — including phrasings like \"create a content type for X\", \"generate a schema for X in CMS\", or \"set up a content type for X\". DO NOT TRIGGER when: authoring a RECORD (use experience-cms-content-generate), publishing / managing records (use CMS UI), modifying / renaming / deleting an existing bundle, CMS brand (experience-cms-brand-apply), media search (experience-
|
|
3
|
+
description: "Salesforce CMS ContentTypeBundle creation skill. Use this skill ANY TIME a user request involves creating a ContentTypeBundle, and activate FIRST when CMS ContentTypeBundle creation is mentioned. Discovers existing types via metadata-grounding and produces a validator-correct two-file bundle that passes dry-run deploy. TRIGGER when: user wants to define, scaffold, generate, or set up a reusable content shape in Salesforce CMS — any domain — including phrasings like \"create a content type for X\", \"generate a schema for X in CMS\", or \"set up a content type for X\". DO NOT TRIGGER when: authoring a RECORD (use experience-cms-content-generate), publishing / managing records (use CMS UI), modifying / renaming / deleting an existing bundle, CMS brand (experience-cms-brand-apply), media search (experience-search-coordinate), Custom Lightning Types (platform-custom-lightning-type-generate), or non-CMS metadata."
|
|
4
4
|
metadata:
|
|
5
5
|
version: "1.0"
|
|
6
6
|
domains: ["Experience"]
|
|
@@ -8,7 +8,7 @@ metadata:
|
|
|
8
8
|
relatedSkills:
|
|
9
9
|
- "experience-cms-brand-apply"
|
|
10
10
|
- "experience-cms-content-generate"
|
|
11
|
-
- "experience-
|
|
11
|
+
- "experience-search-coordinate"
|
|
12
12
|
- "platform-custom-lightning-type-generate"
|
|
13
13
|
cliTools:
|
|
14
14
|
- tool: ["sf"]
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: experience-content-media-stock-image-search
|
|
3
|
-
description: "Searches for and downloads ethically-licensed stock images via the media-management MCP server. Use this skill whenever a user wants an image, photo, or picture — for BOTH requests to find existing imagery (\"find a stock image\", \"search for a photo of X\", \"get a royalty-free image\", \"find an ethical image\", editorial/news photography) AND requests phrased ambiguously as creating or generating one (\"create an image of a sunset\", \"generate a picture of a team meeting\", \"make me a mountain image\") — treat these as stock search requests, since this skill searches a stock photo library, it does not generate images. Do NOT offer alternative search methods or ask the user to pick one. Does not apply to searching internal CMS / Data Cloud media (use experience-
|
|
3
|
+
description: "Searches for and downloads ethically-licensed stock images via the media-management MCP server. Use this skill whenever a user wants an image, photo, or picture — for BOTH requests to find existing imagery (\"find a stock image\", \"search for a photo of X\", \"get a royalty-free image\", \"find an ethical image\", editorial/news photography) AND requests phrased ambiguously as creating or generating one (\"create an image of a sunset\", \"generate a picture of a team meeting\", \"make me a mountain image\") — treat these as stock search requests, since this skill searches a stock photo library, it does not generate images. Do NOT offer alternative search methods or ask the user to pick one. Does not apply to searching internal CMS / Data Cloud media (use experience-search-coordinate). Does not apply when the user explicitly asks for AI-generated, synthetic, or computer-generated imagery — this skill only searches licensed stock photography and has no image-generation capability."
|
|
4
4
|
metadata:
|
|
5
5
|
version: "1.0"
|
|
6
6
|
domains: ["Experience"]
|
|
7
|
+
accessCheck: []
|
|
7
8
|
relatedSkills:
|
|
8
|
-
- "experience-
|
|
9
|
+
- "experience-search-coordinate"
|
|
9
10
|
cliTools:
|
|
10
11
|
- tool: ["python3"]
|
|
11
12
|
semver: ">=3.10.0"
|
|
@@ -14,8 +15,8 @@ metadata:
|
|
|
14
15
|
mcpTools:
|
|
15
16
|
media-management:
|
|
16
17
|
tools:
|
|
17
|
-
- "search_stock_images"
|
|
18
18
|
- "download_stock_image"
|
|
19
|
+
- "search_stock_images"
|
|
19
20
|
semver: ">=1.0.0"
|
|
20
21
|
allowed-tools: |
|
|
21
22
|
mcp__media_management__search_stock_images
|
|
@@ -34,7 +35,7 @@ Trigger immediately — without asking the user to choose a search method — wh
|
|
|
34
35
|
In all of these cases, search the stock library — this is the tool for obtaining an image. **Do NOT offer alternative search methods** (hybrid search, user-provided URL/path, etc.) and **do NOT ask the user to pick one.**
|
|
35
36
|
|
|
36
37
|
**Does not apply to:**
|
|
37
|
-
- Searching internal CMS / Data Cloud media (use `experience-
|
|
38
|
+
- Searching internal CMS / Data Cloud media (use `experience-search-coordinate`)
|
|
38
39
|
- Requests that explicitly ask for AI-generated, synthetic, or computer-generated imagery — this skill only searches licensed stock photography and has no image-generation capability
|
|
39
40
|
|
|
40
41
|
---
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: experience-search-coordinate
|
|
3
|
+
description: "Searches for and retrieves existing content and media (articles, blogs, news, FAQs, events, products, images, logos, icons, photos, graphics, banners, hero images, audio clips, videos, documents) from Salesforce CMS or other connected sources. Use this skill ANY TIME a user says \"find\", \"search\", \"get\", \"fetch\", \"retrieve\", \"look up\", \"locate\", \"show me\", \"list\", or \"I need\" followed by content or media words like \"blog\", \"article\", \"news\", \"FAQ\", \"event\", \"product page\", \"image\", \"logo\", \"icon\", \"photo\", \"banner\", \"audio\", \"video\", \"document\". Also use for requests that mix both, like \"reuse existing assets\" for a page. Routes each intent to the correct sub-workflow — structured content search or media search — and can run both in the same request. Does not apply when the request is to generate NEW content or images from scratch, define content type schemas, or apply brand guidelines."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
domains: ["Experience"]
|
|
7
|
+
relatedSkills:
|
|
8
|
+
- "experience-cms-brand-apply"
|
|
9
|
+
- "experience-cms-content-generate"
|
|
10
|
+
- "experience-cms-content-render"
|
|
11
|
+
- "experience-cms-content-type-generate"
|
|
12
|
+
mcpTools:
|
|
13
|
+
content-readonly:
|
|
14
|
+
tools:
|
|
15
|
+
- get_content_types_for_workspace
|
|
16
|
+
- search_content_cms_channels
|
|
17
|
+
- search_electronic_media
|
|
18
|
+
- search_media_cms_channels
|
|
19
|
+
semver: ">=1.0.0"
|
|
20
|
+
metadata-grounding:
|
|
21
|
+
tools:
|
|
22
|
+
- query_metadata
|
|
23
|
+
- search_metadata
|
|
24
|
+
semver: ">=1.0.0"
|
|
25
|
+
skill_bridge:
|
|
26
|
+
tools:
|
|
27
|
+
- load_skill
|
|
28
|
+
semver: ">=1.0.0"
|
|
29
|
+
cliTools:
|
|
30
|
+
- tool: ["sf"]
|
|
31
|
+
semver: ">=2.0.0"
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# Search Coordinator
|
|
35
|
+
|
|
36
|
+
Routes search requests to the correct sub-workflow — structured CMS content or media — and runs both when a request mixes the two.
|
|
37
|
+
|
|
38
|
+
**A `→ Read` pointer below is a required action, not FYI** — open that file before continuing past that point; this skill has drifted before from steps followed off memory.
|
|
39
|
+
|
|
40
|
+
## Scope
|
|
41
|
+
|
|
42
|
+
**This skill is for SEARCHING FOR existing content or media, not creating new content or images.**
|
|
43
|
+
|
|
44
|
+
**Use this skill when the user wants to:**
|
|
45
|
+
|
|
46
|
+
- Search for articles, blog posts, news, FAQs, events, products, or custom content
|
|
47
|
+
- Search for images, logos, icons, photos, graphics, banners, hero images, audio clips, videos, or documents
|
|
48
|
+
- Reuse existing assets instead of creating new ones, or any combination of the above in one request
|
|
49
|
+
|
|
50
|
+
**DO NOT use this skill when the user wants to:**
|
|
51
|
+
|
|
52
|
+
- Generate new content (use `experience-cms-content-generate`)
|
|
53
|
+
- Generate new images (use image generation tools)
|
|
54
|
+
- Define content type schemas (use `experience-cms-content-type-generate`)
|
|
55
|
+
- Apply brand guidelines (use `experience-cms-brand-apply`)
|
|
56
|
+
- Create, edit, or publish content records
|
|
57
|
+
|
|
58
|
+
## Step 1: Determine Channel Scope
|
|
59
|
+
|
|
60
|
+
**Mandatory on every request that reaches it — never skip straight to Step 2.** No phrasing of the request ("just find me an article," a plain keyword search, etc.) skips this step's local-file scan. If you're about to call `search_metadata` in Step 2 without having scanned for `uiBundles/*/public/content-metadata.json` this turn, STOP and come back here first.
|
|
61
|
+
|
|
62
|
+
1. **Explicit `channelId` given** — use it directly, skip discovery entirely, proceed to the scope rule below.
|
|
63
|
+
2. **Named uiBundle/app, no `channelId`** — resolve that uiBundle's `channelId`; if it doesn't resolve, fall through to point 3 as if nothing was named.
|
|
64
|
+
3. **Otherwise, discover from the local project** — scan every `uiBundles/*/public/content-metadata.json` file (never gate this on server-side deployment status) and collect the `channelId`s found. Zero → public channels only. Exactly one → public plus that channel. More than one → ask the user which one via `ask_followup_question`.
|
|
65
|
+
|
|
66
|
+
→ Read `references/scope-resolution.md` § "Step 1 points 1-3" for the full discovery mechanics before acting on points 1-3 above. Proceed to Step 2 once scope is determined.
|
|
67
|
+
|
|
68
|
+
**Scope rule (applies whether the `channelId` was user-provided or discovered):** scope is always public channels (`contentAccessScope: "Public"`, no `channelIds` field) plus that one `channelId` (a separate call with `channelIds` set to it, no `contentAccessScope` field) — never resolve a separate ID to represent "public," and never send `contentAccessScope` and `channelIds` in the same call. **This scope decides how many search calls Steps 3 and 4 make:** no `channelId` in scope → one search call (public channels only). A `channelId` in scope → two search calls (public channels, then that specific `channelId`), with results merged before presenting.
|
|
69
|
+
|
|
70
|
+
## Step 2: Determine Content Type and Route
|
|
71
|
+
|
|
72
|
+
**Precondition: Step 1 must have already run in this turn** (its channel-scope result is what point 5 below carries into Step 3/4). Step 1 runs unconditionally — even for a plain keyword search with no obvious channel angle, not only when the request names a channel or app.
|
|
73
|
+
|
|
74
|
+
1. **Identify one or more distinct search intents** in the request (e.g. "find a press release about our launch and a hero image for the page" is two intents). Run the rest of this step once per intent.
|
|
75
|
+
|
|
76
|
+
**If intents span both routes, sequence the routes — never interleave or merge them:** run the Media Route (Step 4) to full completion for every media intent first, then the Content Route (Step 3) for every content intent. See Step 5.
|
|
77
|
+
2. **Call `search_metadata`** on the `metadata-grounding` MCP server using the intent itself as the query. **No `filters` array support** — `metadataType` is the only narrowing parameter, passed top-level:
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
search_metadata({ query: "<the user's intent>", metadataType: "ContentTypeBundle", limit: 100 })
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Each candidate already includes a full composite identifier (e.g. `00DVW00000CfBQP2A3::0T1VW000008sow60AA::ContentTypeBundle`) — use it as returned; never reconstruct it.
|
|
84
|
+
|
|
85
|
+
**Check the outcome before doing anything else:**
|
|
86
|
+
- **`metadata-grounding` is unavailable (the call errors/times out)** — you MUST call `get_content_types_for_workspace` (the workspace content-type fallback) before any Step 3/4 search tool; do not attempt `query_metadata`, there's nothing to look up, and do not skip straight to a search tool on an assumed FQN.
|
|
87
|
+
- **Zero candidates returned** — ask the user one clarifying question to narrow the intent (`ask_followup_question`, same question/options as the "intent itself is ambiguous" case at the end of this step), then retry `search_metadata` once with the clarified query:
|
|
88
|
+
- **Still zero candidates after that one retry** — apply the workspace content-type fallback. Do not ask a second clarifying question.
|
|
89
|
+
- **One or more candidates on the retry** — continue to point 3.
|
|
90
|
+
- **One or more candidates on the first call** — continue to point 3.
|
|
91
|
+
|
|
92
|
+
**Fallback chain when `search_metadata` doesn't produce a usable candidate list** → Read `references/content-type-discovery.md` before running it: "Workspace content-type fallback" first, then "Wording fallback" as a last resort.
|
|
93
|
+
|
|
94
|
+
3. **Call `query_metadata`** with each candidate's identifier from point 2 (cap at top 5, batched into one `metadataIdentifiers` array):
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
query_metadata({ metadataIdentifiers: ["00DVW00000CfBQP2A3::0T1VW000008sow60AA::ContentTypeBundle"] })
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Read each entry's `typeDetails[].properties` — `mixins`, `typeClasses`, and `apiName` live there, not in the `search_metadata` response.
|
|
101
|
+
|
|
102
|
+
4. **Filter for supported types first, THEN decide the route among survivors — do not conflate the two:**
|
|
103
|
+
- **Discard** any candidate whose `properties.mixins` lacks `sfdc_cms:deliveryApiEnabled` — only delivery-API-enabled types are searchable.
|
|
104
|
+
- **Of the remainder, discard any whose `properties.typeClasses` lacks `sfdc_cms__structured`.** This is the single "supported for search" gate, not a content/media split — `sfdc_cms__structured` appears on both structured-content types and media types, so it only separates searchable types from unsearchable ones (e.g. `sfdc_cms__email` carries `sfdc_cms__content` but not `sfdc_cms__structured`, and must be discarded).
|
|
105
|
+
- **Zero survive** — tell the user which types were found but aren't supported, then stop (don't guess a route). **Some, but not all, survive** — tell the user which got filtered out (plain statement, not a question), then continue with survivors. **All survive** — no message needed. → Read `references/content-type-discovery.md` § "Filter-result messages" for exact wording.
|
|
106
|
+
- **Route on the top-ranked survivor's `properties.typeClasses`** (order returned by `search_metadata`): contains `sfdc_cms__media` → Media Route (Step 4); otherwise → Content Route (Step 3).
|
|
107
|
+
- **Collect the FQN(s)** — the top-ranked survivor's `properties.apiName`. Add other surviving candidates' FQNs too if clearly relevant to the same intent and sharing the same classification, passing all as one comma-separated `contentTypeFqn` value rather than searching per type.
|
|
108
|
+
5. **Carry the scope from Step 1** (public channels only, or public channels plus a `channelId`) into whichever route is invoked — see the `channelIds` field in each route below.
|
|
109
|
+
|
|
110
|
+
**If intent itself is ambiguous** (e.g. "find me some content" with no hint of type or subject) → Read `references/content-type-discovery.md` § "Intent itself is ambiguous" for the exact question/options before calling `search_metadata`. Do not guess.
|
|
111
|
+
|
|
112
|
+
## Step 3: Content Route
|
|
113
|
+
|
|
114
|
+
Follow this sequence for each intent Step 2 routed here. The content type FQN(s) are already known from Step 2.
|
|
115
|
+
|
|
116
|
+
1. **Build the search query:**
|
|
117
|
+
- **Extract keywords** — concrete nouns and named subjects that would appear in the content's title or body. Use domain-specific synonyms, max 10 terms, joined with ` OR `. E.g. "blog posts about sustainable business practices" → `sustainable OR sustainability OR business OR environmental`.
|
|
118
|
+
- **Extract taxonomies** — descriptive qualities, categories, or topical tags only (adjectives, categories, attributes — not concrete nouns). Stringify as `{"OR": ["term1", "term2"]}`, or `"{}"` if none apply. E.g. "sustainable business practices and environmental responsibility" → `{"OR": ["Business", "Corporate", "Environment", "Responsibility"]}`; "FAQs about password resets" → `"{}"` (no descriptive/categorical terms).
|
|
119
|
+
- Load `assets/search-payload-template.json` for the request payload structure. Set `contentTypeFqn` to the comma-separated FQN list collected in Step 2. `contentAccessScope` and `channelIds` are **mutually exclusive** — never send both in the same call — so they're set per-call in point 3 below, not here.
|
|
120
|
+
2. **Confirm with the user** — required two-message pattern (labeled-parameters chat message, then `ask_followup_question` with `Yes - search now` / `Edit search` / `Cancel`). Never put the labeled list inside `ask_followup_question`'s text — it strips formatting. → Read `references/content-route.md` § "Confirming the query before searching" for templates.
|
|
121
|
+
3. **Execute the search call(s)** per the scope from Step 1 — one call for public-only scope, two (public + channel-scoped) when a `channelId` is in scope. → Read `references/content-route.md` § "Executing the search call(s)" before parsing results — array/merge rules.
|
|
122
|
+
4. **Present up to 5 unique results total** (de-duplicated by `managedContentKey` for display only — an item found under more than one channel still appears once here), grouped by content type, with a `Load More` option if more remain. Each item shows its **Channel** as a per-item field — never the grouping key:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
I found 3 content items:
|
|
126
|
+
|
|
127
|
+
Editorial (c__Editorial):
|
|
128
|
+
1. <title> — Channel: Public
|
|
129
|
+
2. <title> — Channel: <uiBundle>
|
|
130
|
+
|
|
131
|
+
Blog (sfdc_cms__blog):
|
|
132
|
+
3. <title> — Channel: Public
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Only one content type FQN in scope → the heading may be omitted (still one group); the per-item Channel field is always shown. If an item exists under multiple channels, show its first/primary channel here — the choice between channels happens after selection (point 5), not at presentation time.
|
|
136
|
+
|
|
137
|
+
5. **User selects** one, several, or all items. **For each selected item whose delivery-details array has more than one entry**, ask which channel's copy to use — one `ask_followup_question` option per entry, named from `managedContentChannelDetails.name` — before displaying its details; items with zero or one entry go straight to display. Show `managedContentId`, `managedContentKey`, `language`, channel, and — only for a `PUBLIC_UNAUTHENTICATED`/`COMMUNITY` entry with a `contentUrl`, never a `WEB_APP` one — the full **Unauthenticated URL** (all query parameters). → Read `references/content-route.md` § "Disambiguating channel at selection" before asking — pass the channel names as `ask_followup_question`'s `options` array, never inline them in the question text.
|
|
138
|
+
6. **If the channelId in scope came from uiBundle discovery** (skip entirely for public-only scope or a user-supplied `channelId` with no uiBundle), offer the `experience-cms-content-render` hand-off using the same two-message pattern, passing each item's `title`, content type FQN, its chosen entry's `managedContentChannelDetails.id` (channel ID) **only when it has a delivery entry — omit for a zero-entry item, never a placeholder**, and either its Unauthenticated URL or, if absent, its `managedContentKey` **as `contentKey`** (the receiving skill's field name). → Read `references/content-route.md` § "Offering the render hand-off" for the full procedure.
|
|
139
|
+
|
|
140
|
+
→ Read `references/content-route.md` for the complete end-to-end worked example.
|
|
141
|
+
|
|
142
|
+
## Step 4: Media Route
|
|
143
|
+
|
|
144
|
+
Follow this sequence for each intent Step 2 routed here. The media content type FQN (e.g. `sfdc_cms__image`, `sfdc_cms__audio`, `sfdc_cms__video`, `sfdc_cms__document`) is already known from Step 2 — never assume it's an image; use whatever FQN Step 2 discovered for this intent.
|
|
145
|
+
|
|
146
|
+
1. **Present available search sources via `ask_followup_question` — no other tool calls yet.** Only the sources whose tools you actually have (via introspection), plus "Other", each as its own option. Wait for the user's selection before any tool call. → Read `references/media-route.md` § "Presenting available search sources" for the full template.
|
|
147
|
+
2. **Build the query, then confirm with the user before searching — required two-message pattern**, in the same turn:
|
|
148
|
+
- **Search using keywords** (`search_media_cms_channels`): extract keywords (concrete nouns, max 10 terms) and taxonomies (descriptive/style/mood/category terms — extract every one the query actually contains; don't default to empty just because keywords were found). E.g. "luxury apartment with river view" → keywords `apartment OR villa OR penthouse OR residence`, taxonomies `{"OR": ["Luxury", "Premium", "Waterfront", "Riverside"]}`; "car" → taxonomies `"{}"` (no descriptive terms). Build the payload with `contentTypeFqn` set to the FQN(s) from Step 2, `searchLanguage` set to a locale (`en_US`, `es_MX`, ... — default `en_US`; **required, never null/omitted**), `searchLimit: 5`. → Read `references/media-route.md` for full worked examples.
|
|
149
|
+
- **Search using Data 360 hybrid search** (`search_electronic_media`) — pass the user's query as-is to `searchQuery`, no extraction needed.
|
|
150
|
+
- **Other** — ask for a direct URL, asset library path, or specific system to check (no confirmation step, since no tool is called).
|
|
151
|
+
|
|
152
|
+
→ Read `references/media-route.md` § "Confirming the query before searching" for the confirmation message templates (labeled-parameters chat message, then `ask_followup_question` with `Yes - search now` / `Edit search` / `Cancel`).
|
|
153
|
+
3. **Execute the search call(s)** per the scope from Step 1 — one call for public-only scope, two (public + channel-scoped) when a `channelId` is in scope; Data 360 hybrid search ignores channel scope entirely. → Read `references/media-route.md` § "Executing the search call(s)" before parsing results — array/merge rules.
|
|
154
|
+
4. **Present all unique results via `ask_followup_question`, grouped by content type** — one option per result (de-duplicated by unique identifier for display only — an item found under more than one channel still appears once here), labeled with title, content type, and channel (e.g. `Product Launch Hero Banner (sfdc_cms__image) — Public`). Mirrors the Content Route: content type is the grouping key, channel is a per-item field. Never auto-select.
|
|
155
|
+
5. **User selects** an item. **If its delivery-details array has more than one entry**, ask which channel's copy to use — one option per entry, named from `managedContentChannelDetails.name` — before displaying details. Show Content Name, Content Type, channel, and — only for a `PUBLIC_UNAUTHENTICATED`/`COMMUNITY` entry with a `contentUrl`, never a `WEB_APP` one — the full **Unauthenticated URL** (all query parameters). → Read `references/media-route.md` § "Disambiguating channel at selection" before asking — pass the channel names as `ask_followup_question`'s `options` array, never inline them in the question text.
|
|
156
|
+
6. **Offer the render hand-off** — always, with no uiBundle gating (unlike the Content Route's offer, which only appears when a uiBundle channel was in scope). Same two-message pattern: plain-text mention of the item, then `ask_followup_question` with `Yes` / `No`. On `Yes`, dispatch via `mcp__skill_bridge__load_skill("experience-cms-content-render")` — never the built-in Skill tool (separate registry, returns `Unknown skill`) — passing the selected item's `title`, content type FQN, the chosen entry's `managedContentChannelDetails.id` (channel ID) **only when it has a delivery entry — this route explicitly supports zero-entry items, so omit the channel ID for those rather than reading `.id` off nothing**, and either its Unauthenticated URL or, if absent, its `managedContentKey` **as `contentKey`** (the receiving skill's field name). If the load call fails, tell the user the hand-off failed and stop. On `No`, stop — the search is complete; do not apply the URL yourself as a fallback.
|
|
157
|
+
|
|
158
|
+
→ Read `references/media-route.md` for the complete worked example.
|
|
159
|
+
|
|
160
|
+
## Step 5: Present Combined Results
|
|
161
|
+
|
|
162
|
+
When a request mixes media and content intents, run the two routes **sequentially, each to full completion, not interleaved and not merged into one combined presentation**:
|
|
163
|
+
|
|
164
|
+
1. **Media Route first** — for every media intent, run Step 4 in full: present sources, build/confirm the query, search, present results, let the user select, and (point 6) offer the `experience-cms-content-render` hand-off (Yes/No). Finish this entire route — including the offer — before starting the Content Route.
|
|
165
|
+
2. **Content Route second** — for every content intent, run Step 3 in full: build/confirm the query, search, present results grouped by content type (channel shown per-item), let the user select, and (point 6) offer the `experience-cms-content-render` hand-off if a uiBundle channel is in scope.
|
|
166
|
+
|
|
167
|
+
Each route's results are labeled and presented on their own (e.g. "Images:" for the Media Route's output, then "Articles:" for the Content Route's) — never merge the two into one undifferentiated list, and never hold Media Route's results back to present alongside Content Route's.
|
|
168
|
+
|
|
169
|
+
## Error Handling
|
|
170
|
+
|
|
171
|
+
| Error | Response |
|
|
172
|
+
|---|---|
|
|
173
|
+
| No `search_content_cms_channels`/`search_media_cms_channels` tools found via introspection | "No content search connection is configured. Please connect a content-readonly MCP server." |
|
|
174
|
+
| Neither media tool available | "No automated media search sources are currently configured. Please provide a direct URL or asset library path." |
|
|
175
|
+
| `metadata-grounding` unavailable or no content types match | Apply the fallback chain in `references/content-type-discovery.md` — do not block or ask for an FQN |
|
|
176
|
+
| No search results (either route) | "No [content/media] found. Try broader keywords, removing descriptive terms, or a different [content type/source]." |
|
|
177
|
+
| Invalid user selection | Re-display options and ask again |
|
|
178
|
+
| Tool returns error (invalid input, missing argument, or otherwise) | Show error message, offer retry with corrected parameters **on that same tool — never fall through to the other route's search tool as a workaround** (see Key Principle 3) |
|
|
179
|
+
| Search tool on the connected server returns a server error (5xx, timeout, connection failure — not "no results") | **Fail the call — do NOT silently retry against a different server.** Tell the user to check that server's connection/session and wait before retrying. |
|
|
180
|
+
|
|
181
|
+
**Never silently fail on a route the user asked for.** If one route in a mixed request comes up empty, still present the other route's results and say so explicitly. This does not mean substituting a different server when the connected one errors — see the server-error row above.
|
|
182
|
+
|
|
183
|
+
## Key Principles
|
|
184
|
+
|
|
185
|
+
1. **Resolve channel scope first** — determine channel scope (Step 1) before any content-type discovery or search
|
|
186
|
+
2. **Search first, route second** — `search_metadata` + `query_metadata` decide the route; fall back to `get_content_types_for_workspace`, then intent wording, only when that comes up empty (Step 2)
|
|
187
|
+
3. **Never cross-route, including on failure** — content requests never hit media search tools and vice versa; if a route's search tool errors for any reason, fix and retry that same tool or stop and report the error — never fall through to the other route's tool as a workaround
|
|
188
|
+
4. **Metadata grounding is mandatory for content** — discover content type FQNs dynamically, never hardcode them
|
|
189
|
+
5. **Media route starts text-only** — present sources and wait before any tool call
|
|
190
|
+
6. **Confirm before searching (both routes)** — user approves query parameters before execution
|
|
191
|
+
7. **Never auto-select** — always wait for user choice, across channel scope and both routes
|
|
192
|
+
8. **Run mixed requests sequentially, Media Route first** — complete it fully (including the render offer) before starting the Content Route; present each route's results as its own labeled set, never merged
|
|
193
|
+
9. **Handle errors gracefully** — clear feedback and alternatives, per route
|
|
194
|
+
10. **Offer to render, don't assume** — both routes require Yes/No confirmation before the hand-off; only the gating differs — Content Route offers it only when a uiBundle channel was actually in scope, Media Route always offers it
|
|
195
|
+
11. **Never fail over to a different server on a server error** — fail the call and tell the user to check that server's session; don't silently retry elsewhere
|
|
196
|
+
12. **Group by content type, never by channel** — in both routes, channel is always a per-item field, never the group heading
|
|
197
|
+
13. **Disambiguate channel at selection, not before** — an item is shown once at presentation regardless of its delivery-details array length; only after selection do you ask which channel's copy to use, one option per entry by `managedContentChannelDetails.name`, in both routes
|
|
198
|
+
14. **Unauthenticated URL is public-channel-only** — `PUBLIC_UNAUTHENTICATED`/`COMMUNITY` entries only; never a `WEB_APP` (uiBundle) entry's `contentUrl`, in either route
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"public-channels-call": {
|
|
3
|
+
"inputs": [{
|
|
4
|
+
"searchKeyword": "keyword1 OR keyword2 OR keyword3",
|
|
5
|
+
"taxonomyExpression": "{\"OR\": [\"Taxonomy1\", \"Taxonomy2\"]}",
|
|
6
|
+
"searchLanguage": "en_US",
|
|
7
|
+
"contentAccessScope": "Public",
|
|
8
|
+
"contentTypeFqn": "<discovered-fqn, or a comma-separated list e.g. c__Blog,c__Article>",
|
|
9
|
+
"pageOffset": 0,
|
|
10
|
+
"searchLimit": 5
|
|
11
|
+
}]
|
|
12
|
+
},
|
|
13
|
+
"channel-scoped-call": {
|
|
14
|
+
"inputs": [{
|
|
15
|
+
"searchKeyword": "keyword1 OR keyword2 OR keyword3",
|
|
16
|
+
"taxonomyExpression": "{\"OR\": [\"Taxonomy1\", \"Taxonomy2\"]}",
|
|
17
|
+
"searchLanguage": "en_US",
|
|
18
|
+
"channelIds": "<the single discovered uiBundle channelId>",
|
|
19
|
+
"contentTypeFqn": "<discovered-fqn, or a comma-separated list e.g. c__Blog,c__Article>",
|
|
20
|
+
"pageOffset": 0,
|
|
21
|
+
"searchLimit": 5
|
|
22
|
+
}]
|
|
23
|
+
},
|
|
24
|
+
"_note": "contentAccessScope and channelIds are mutually exclusive — never send both in the same call. Use contentAccessScope: \"Public\" for the public-channels call (omit channelIds entirely). Use channelIds set to the discovered channelId for the channel-scoped call (omit contentAccessScope entirely). Issue one call per scope in play."
|
|
25
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Content Route — Full Reference
|
|
2
|
+
|
|
3
|
+
## Confirming the query before searching
|
|
4
|
+
|
|
5
|
+
Required two-message pattern, same turn:
|
|
6
|
+
|
|
7
|
+
**Message 1 (chat-visible plain text — NOT inside `ask_followup_question`):** print the parameters as a labeled list, one field per line:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
I'll search for content with these parameters:
|
|
11
|
+
|
|
12
|
+
Content Type: <label> (<fqn>[, <label2> (<fqn2>), ...])
|
|
13
|
+
Keywords: <keyword1 OR keyword2 OR ...>
|
|
14
|
+
Taxonomies: <term1, term2, ...>
|
|
15
|
+
Language: <en_US>
|
|
16
|
+
Channels: Public[, plus <uiBundle> channel]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Message 2 (immediately after, same turn):** dispatch `ask_followup_question` with a short question and options — `Yes - search now` / `Edit search` / `Cancel`.
|
|
20
|
+
|
|
21
|
+
**Never put the labeled list inside `ask_followup_question`'s `question` text.** That UI strips line breaks and formatting, which is exactly what collapses the parameters back into a single paragraph.
|
|
22
|
+
|
|
23
|
+
## Executing the search call(s)
|
|
24
|
+
|
|
25
|
+
Per the scope determined in Step 1. `contentAccessScope` and `channelIds` are **mutually exclusive fields — never include both in the same call**:
|
|
26
|
+
- **No `channelId` in scope (public channels only)** — one call to `search_content_cms_channels` with `contentAccessScope: "Public"` and no `channelIds` field at all.
|
|
27
|
+
- **A `channelId` is in scope** — two calls: one with `contentAccessScope: "Public"` and no `channelIds` (public channels), and one with `channelIds` set to the discovered `channelId` and no `contentAccessScope` field at all. Each result item already carries its own list of channel deliveries in `managedContentChannelDeliveryDetails` — this array can hold many entries for a single item (a public-unauthenticated channel, a Community channel, several uiBundle channels, etc. — see the real API response below), independent of how many calls were made. **If the same item (by `managedContentKey`) appears in both responses, merge their `managedContentChannelDeliveryDetails` arrays** rather than picking one response and discarding the other — de-duplicate only exact repeats of the same `managedContentChannelDetails.id`. Presentation (Step 3 point 4) still shows the item once; selection (Step 3 point 5) is where the channel choice actually gets made — see "Disambiguating channel at selection" below.
|
|
28
|
+
|
|
29
|
+
From each response, parse `outputValues.searchResults[]`, extracting `title`, `contentType`, `managedContentId`, `managedContentKey`, `language`, and the **full `managedContentChannelDeliveryDetails` array** — never just index `[0]`. Each entry pairs a `contentUrl` with `managedContentChannelDetails.{id, name, type}` (the channel it's delivered to). `type` is one of `PUBLIC_UNAUTHENTICATED`, `COMMUNITY`, or `WEB_APP` (a uiBundle-scoped channel — this is what the channel-scoped call in Step 1 discovers). A single item's array can be empty (not yet delivered to any channel — omit the Unauthenticated URL line entirely for that item), have one entry (use it directly, no question needed), or have several (ask which channel — see "Disambiguating channel at selection" below).
|
|
30
|
+
|
|
31
|
+
**The Unauthenticated URL is public-channel-only.** Only a `PUBLIC_UNAUTHENTICATED` or `COMMUNITY` entry's `contentUrl` is ever shown to the user as the Unauthenticated URL. A `WEB_APP` entry's `contentUrl` is **never** surfaced — omit the URL line entirely when the chosen (or only) entry's type is `WEB_APP`, even though the field is present in the response. This applies after channel disambiguation (below), not before — a `WEB_APP` entry is still offered as a selectable channel option; it just never produces a displayed URL once chosen.
|
|
32
|
+
|
|
33
|
+
**Real API response — `WEB_APP` (uiBundle) channel, from the channel-scoped call:**
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"title": "Apple Inc.",
|
|
38
|
+
"contentType": "c__StockMarket",
|
|
39
|
+
"managedContentId": "20YVW000001miuD",
|
|
40
|
+
"managedContentKey": "MCICPC4X5KE5ATHMTN4EYK5DOCYM",
|
|
41
|
+
"managedContentChannelDeliveryDetails": [
|
|
42
|
+
{
|
|
43
|
+
"contentUrl": "https://.../channels/0apVW000000Z25NYAS/contents/20YVW000001miuD?language=en_US",
|
|
44
|
+
"managedContentChannelDetails": { "id": "0apVW000000Z25N", "name": "testinternalapp", "type": "WEB_APP" }
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Even though `contentUrl` is present, this item's only entry is `WEB_APP` — the Unauthenticated URL line is omitted for it. Only `managedContentId`, `managedContentKey`, `language`, and channel (`testinternalapp`) are shown.
|
|
51
|
+
|
|
52
|
+
**Real API response** (one item, six delivery entries, from a single call — no second channel-scoped call was even involved):
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"title": "Housing Prices In Japan",
|
|
57
|
+
"contentType": "sfdc_cms__news",
|
|
58
|
+
"managedContentKey": "MCVL5CJAB4NBBBRPTMY2N3DALBB4",
|
|
59
|
+
"managedContentChannelDeliveryDetails": [
|
|
60
|
+
{ "contentUrl": "https://.../0apVW000000TsHxYAK/...", "managedContentChannelDetails": { "id": "0apVW000000TsHx", "name": "Test CMS Channel", "type": "PUBLIC_UNAUTHENTICATED" } },
|
|
61
|
+
{ "contentUrl": "https://.../0apVW000000VUt3YAG/...", "managedContentChannelDetails": { "id": "0apVW000000VUt3", "name": "testLWR", "type": "COMMUNITY" } },
|
|
62
|
+
{ "contentUrl": "https://.../0apVW000000ZYoPYAW/...", "managedContentChannelDetails": { "id": "0apVW000000ZYoP", "name": "Test Channel 2", "type": "PUBLIC_UNAUTHENTICATED" } },
|
|
63
|
+
{ "contentUrl": "https://.../0apVW000000ZYq1YAG/...", "managedContentChannelDetails": { "id": "0apVW000000ZYq1", "name": "Test Channel 3", "type": "PUBLIC_UNAUTHENTICATED" } },
|
|
64
|
+
{ "contentUrl": "https://.../0apVW000000ZYrdYAG/...", "managedContentChannelDetails": { "id": "0apVW000000ZYrd", "name": "Test Channel 4", "type": "PUBLIC_UNAUTHENTICATED" } },
|
|
65
|
+
{ "contentUrl": "https://.../0apVW000000ZYtFYAW/...", "managedContentChannelDetails": { "id": "0apVW000000ZYtF", "name": "Test Channel 5", "type": "PUBLIC_UNAUTHENTICATED" } }
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The multi-channel case is not "one entry from the public call, one from the channel-scoped call" — it's whatever the array actually contains, however many entries that is.
|
|
71
|
+
|
|
72
|
+
## Disambiguating channel at selection
|
|
73
|
+
|
|
74
|
+
Reached at Step 3 point 5, only for a selected item whose (merged, per "Executing the search call(s)" above) `managedContentChannelDeliveryDetails` array has more than one entry. Build one option per entry, using that entry's `managedContentChannelDetails.name`, in the order the array returns them — not a fixed two-option choice. **Call `ask_followup_question` with these as its actual `question` and `options` parameters — never concatenate the channel list into the question string itself**, the same rule every other question in this skill follows:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
question: "Which channel would you like to use for 'Sustainability Report 2024'?"
|
|
78
|
+
options: ["Public", "landing-page"]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Wait for the reply before displaying that item's details, and use the record from the **chosen** entry — never assume entry `[0]` is the right one. An item selected alongside it whose array has zero or one entry does **not** get a question — zero entries goes straight to display with the Unauthenticated URL line omitted; one entry goes straight to display using that entry. Once every multi-entry item in the selection has been resolved (or immediately, if none were), display all selected items' details (Step 7 below).
|
|
82
|
+
|
|
83
|
+
**Real-world scale** — the "Housing Prices In Japan" response above would produce:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
question: "Which channel would you like to use for 'Housing Prices In Japan'?"
|
|
87
|
+
options: ["Test CMS Channel", "testLWR", "Test Channel 2", "Test Channel 3", "Test Channel 4", "Test Channel 5"]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Ask from the full array every time, however many entries it has — don't special-case it down to "Public vs. the one uiBundle channel." All six names go in the `options` array; the `question` string stays that one short sentence.
|
|
91
|
+
|
|
92
|
+
**A `WEB_APP` entry is still a valid, selectable option here** — it's excluded only from the Unauthenticated URL line at display time (see "Executing the search call(s)" above), not from the channel choice itself.
|
|
93
|
+
|
|
94
|
+
## Offering the render hand-off
|
|
95
|
+
|
|
96
|
+
Only when the channelId in scope came from uiBundle discovery (skip entirely for public-only scope, or a user-supplied `channelId` with no associated uiBundle). Same required two-message pattern as the search confirmation above:
|
|
97
|
+
|
|
98
|
+
**Message 1 (plain text):**
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
I can render the following in the <uiBundle> app:
|
|
102
|
+
|
|
103
|
+
1. <title> (<content type label>)
|
|
104
|
+
2. <title> (<content type label>)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Message 2 (immediately after):** `ask_followup_question` with options `Yes` / `No`. Never restate the item list or count inside the question text — the question stays generic (`"Render this content in the <uiBundle> app?"`).
|
|
108
|
+
|
|
109
|
+
- **Yes** — dispatch via `mcp__skill_bridge__load_skill("experience-cms-content-render")` — never the built-in Skill tool (separate registry, returns `Unknown skill`). Pass each selected item's `title`, content type FQN, its chosen entry's `managedContentChannelDetails.id` (channel ID) **only when that item's (merged) `managedContentChannelDeliveryDetails` array has an entry** — use the chosen entry if the array had more than one, or the sole entry if it had exactly one. **An item with zero entries has no entry to read `.id` from — omit the channel ID field for it entirely, never a placeholder or `null`**; `experience-cms-content-render`'s own documented channel-resolution flow asks the user for the channel (or HALTs) when none is supplied, so this is safe to leave out. Also pass either its Unauthenticated URL or, if absent, its `managedContentKey` renamed to `contentKey` — `experience-cms-content-render`'s own `SKILL.md` names the field `contentKey`, not `managedContentKey`. If the load call fails, tell the user the hand-off failed and stop — don't render yourself.
|
|
110
|
+
- **No** — stop; the search is complete.
|
|
111
|
+
|
|
112
|
+
## Building the search query
|
|
113
|
+
|
|
114
|
+
Keyword/taxonomy extraction rule: see `SKILL.md` Step 3 point 1 (the Media Route uses the same split — see `media-route.md` for media-specific examples). Additional worked examples for content:
|
|
115
|
+
- "press release about our product launch" → keywords: launch, announcement, release, product launch; taxonomies: Launch, Announcement, News
|
|
116
|
+
- "FAQs about password resets" → keywords: password, reset, login, account; taxonomies: _(empty — no descriptive/categorical terms)_
|
|
117
|
+
|
|
118
|
+
1. **Determine locale** — format `en_US`, `es_MX`, `fr_FR` (default: `en_US`)
|
|
119
|
+
2. **Build the JSON payload** — `searchKeyword` joins keywords with ` OR `; `taxonomyExpression` is a stringified `{"OR": ["term1", "term2"]}`, or `"{}"` if none.
|
|
120
|
+
|
|
121
|
+
## Complete End-to-End Example
|
|
122
|
+
|
|
123
|
+
**User Request:**
|
|
124
|
+
> "I need to find blog posts about sustainable business practices and environmental responsibility for our company website."
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
**Step 1: Determine Search Intent**
|
|
129
|
+
|
|
130
|
+
Intent parsed:
|
|
131
|
+
- Content type: Blog posts
|
|
132
|
+
- Keywords: sustainable, business, practices, environmental, responsibility
|
|
133
|
+
- Clear intent → proceed to Step 2
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
**Step 2: Discover Content Types**
|
|
138
|
+
|
|
139
|
+
Call `search_metadata` — this tool has no `filters` array; `metadataType` is the only accepted narrowing parameter:
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"query": "blog post article",
|
|
143
|
+
"metadataType": "ContentTypeBundle",
|
|
144
|
+
"limit": 100
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This returns candidate matches only — no `jsonAttributes`, so routing and delivery-API filtering aren't possible yet. Each candidate already includes a full composite identifier, e.g. `00DVW00000CfBQP2A3::0T1VW000008sow60AA::ContentTypeBundle` (name `sfdc_cms__blog`) among others — use it as returned.
|
|
149
|
+
|
|
150
|
+
Call `query_metadata` with each candidate's identifier from above, as-is, batched into one `metadataIdentifiers` array:
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"metadataIdentifiers": [
|
|
154
|
+
"00DVW00000CfBQP2A3::0T1VW000008sow60AA::ContentTypeBundle"
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
For each entry, read `typeDetails[].properties` — `mixins`, `typeClasses`, and `apiName` live there. Filter in two steps, **before** deciding content-vs-media routing:
|
|
160
|
+
|
|
161
|
+
1. Discard any candidate whose `properties.mixins` lacks `sfdc_cms:deliveryApiEnabled`.
|
|
162
|
+
2. Of the remainder, discard any candidate whose `properties.typeClasses` does not contain `sfdc_cms__structured`. **This is a "supported for search" gate, not a content/media split** — `sfdc_cms__structured` is present on both structured-content types (`sfdc_cms__blog`) and media types (`sfdc_cms__image`), so a surviving candidate could still turn out to be either. **`sfdc_cms__content` alone is not sufficient** here — it's a shared parent class for both structured content and media types (e.g. `sfdc_cms__email` has `sfdc_cms__content` in its `typeClasses` but not `sfdc_cms__structured`, and must be discarded, not routed to content search).
|
|
163
|
+
|
|
164
|
+
Only after this filtering do you inspect the survivors' `typeClasses` again to route: `sfdc_cms__media` present → Media Route (see `media-route.md`); otherwise → Content Route (this file). Rank the content-route survivors.
|
|
165
|
+
|
|
166
|
+
Result: Found `sfdc_cms__blog` content type (single match here after filtering; if multiple relevant types had matched, e.g. `sfdc_cms__blog` and `c__Article`, they would be passed together as `"sfdc_cms__blog,c__Article"`)
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
**Step 3: Build Search Query**
|
|
171
|
+
|
|
172
|
+
Extract:
|
|
173
|
+
- Keywords: `sustainable OR sustainability OR environmental OR responsibility OR green OR eco OR carbon`
|
|
174
|
+
- Taxonomies: `{"OR": ["Business", "Corporate", "Environment", "Responsibility"]}`
|
|
175
|
+
- Language: `en_US`
|
|
176
|
+
|
|
177
|
+
Construct payload (public-channels scope only here — no `channelId` was discovered, so `contentAccessScope` is set and `channelIds` is omitted entirely; the two fields are mutually exclusive):
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"inputs": [{
|
|
181
|
+
"searchKeyword": "sustainable OR sustainability OR environmental OR responsibility OR green OR eco OR carbon",
|
|
182
|
+
"taxonomyExpression": "{\"OR\": [\"Business\", \"Corporate\", \"Environment\", \"Responsibility\"]}",
|
|
183
|
+
"searchLanguage": "en_US",
|
|
184
|
+
"contentAccessScope": "Public",
|
|
185
|
+
"contentTypeFqn": "sfdc_cms__blog",
|
|
186
|
+
"pageOffset": 0,
|
|
187
|
+
"searchLimit": 5
|
|
188
|
+
}]
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
**Step 4: Confirm Query**
|
|
195
|
+
|
|
196
|
+
Present to user:
|
|
197
|
+
```text
|
|
198
|
+
I'll search for content with these parameters:
|
|
199
|
+
|
|
200
|
+
Content Type: Blog (sfdc_cms__blog)
|
|
201
|
+
Keywords: sustainable OR sustainability OR environmental OR responsibility OR green OR eco OR carbon
|
|
202
|
+
Taxonomies: Business, Corporate, Environment, Responsibility
|
|
203
|
+
Language: en_US
|
|
204
|
+
Channels: Public
|
|
205
|
+
|
|
206
|
+
Proceed with this search?
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
User: "Yes - search now"
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
**Step 5: Execute Search**
|
|
214
|
+
|
|
215
|
+
Call `search_content_cms_channels` with the payload
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
**Step 6: Present Results**
|
|
220
|
+
|
|
221
|
+
Results are grouped by content type, with channel shown as a per-item field. This example ran with only one content type FQN (`sfdc_cms__blog`) and public-channels-only scope, so there's only one group and the content-type heading is omitted — but the per-item `Channel:` field is still shown:
|
|
222
|
+
|
|
223
|
+
```text
|
|
224
|
+
I found 5 content items:
|
|
225
|
+
|
|
226
|
+
1. Our Journey to Carbon Neutrality
|
|
227
|
+
Content Type: Blog (sfdc_cms__blog) — Channel: Public
|
|
228
|
+
|
|
229
|
+
2. Building a Sustainable Supply Chain
|
|
230
|
+
Content Type: Blog (sfdc_cms__blog) — Channel: Public
|
|
231
|
+
|
|
232
|
+
3. Renewable Energy: Powering Our Future
|
|
233
|
+
Content Type: Blog (sfdc_cms__blog) — Channel: Public
|
|
234
|
+
|
|
235
|
+
4. Green Office Initiatives That Work
|
|
236
|
+
Content Type: Blog (sfdc_cms__blog) — Channel: Public
|
|
237
|
+
|
|
238
|
+
5. Sustainability Report 2024
|
|
239
|
+
Content Type: Blog (sfdc_cms__blog) — Channel: Public
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
6. Load More (showing 5 of 8)
|
|
243
|
+
7. Done
|
|
244
|
+
|
|
245
|
+
Select one or more items (e.g., "1, 3" or "all"), Load More, or Done:
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
If a `landing-page` uiBundle channel had also been in scope and returned hits, the channel would still just be a per-item field — it would NOT create a new group, since content type is the only grouping key. But if a second content type (e.g. `c__Article`) had also matched, results would be grouped and numbered continuously by content type instead, e.g.:
|
|
249
|
+
|
|
250
|
+
```text
|
|
251
|
+
I found 3 content items:
|
|
252
|
+
|
|
253
|
+
Blog (sfdc_cms__blog):
|
|
254
|
+
1. Our Journey to Carbon Neutrality — Channel: Public
|
|
255
|
+
2. Internal Sustainability Metrics Q3 — Channel: landing-page
|
|
256
|
+
|
|
257
|
+
Article (c__Article):
|
|
258
|
+
3. Building a Sustainable Supply Chain — Channel: Public
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
User: "1, 2, 5"
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
**Step 7: Display Content Details**
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
Selected content:
|
|
269
|
+
|
|
270
|
+
1. Our Journey to Carbon Neutrality
|
|
271
|
+
Content Type: Blog (sfdc_cms__blog)
|
|
272
|
+
Channel: Public
|
|
273
|
+
Content ID: 20YXX000000010
|
|
274
|
+
Content Key: MCBT7N9K5WX2QPFR4VHD8JLZ6MGY
|
|
275
|
+
Language: en_US
|
|
276
|
+
Unauthenticated URL: https://example.my.salesforce-sites.com/blog/carbon-neutrality-journey
|
|
277
|
+
|
|
278
|
+
2. Building a Sustainable Supply Chain
|
|
279
|
+
Content Type: Blog (sfdc_cms__blog)
|
|
280
|
+
Channel: Public
|
|
281
|
+
Content ID: 20YXX000000011
|
|
282
|
+
Content Key: MCQJ3P8TK6DWL9XN5VHR2FZ4GBY7
|
|
283
|
+
Language: en_US
|
|
284
|
+
Unauthenticated URL: https://example.my.salesforce-sites.com/blog/sustainable-supply-chain
|
|
285
|
+
|
|
286
|
+
5. Sustainability Report 2024
|
|
287
|
+
Content Type: Blog (sfdc_cms__blog)
|
|
288
|
+
Channel: Public
|
|
289
|
+
Content ID: 20YXX000000014
|
|
290
|
+
Content Key: MCHZ8P3T7FN5XDWK2QJVR6GMLBY9
|
|
291
|
+
Language: en_US
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Item 5's `managedContentChannelDeliveryDetails` array is empty (not yet delivered to any channel), so its URL line is omitted rather than shown blank. Items 1 and 2 have one entry each, so their URLs are shown in full. (This example's scope was public-channels-only with no `channelId` discovered — see SKILL.md Step 1 — so no item here has more than one delivery entry; for a worked multi-entry disambiguation, see "Disambiguating channel at selection" above.)
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
**Step 8: Offer to Render (only if a uiBundle channel was in scope)**
|
|
299
|
+
|
|
300
|
+
This example ran with public-channels-only scope (SKILL.md Step 1 found zero uiBundle `channelId`s), so this step is skipped entirely — the results from Step 7 are the final output. If SKILL.md Step 1 had found, say, a `landing-page` uiBundle with a `channelId`, the agent would instead use the same two-message pattern as Step 4's search confirmation — the item list as its own plain-text message, never folded into `ask_followup_question`'s question text:
|
|
301
|
+
|
|
302
|
+
**Message 1 (plain chat text):**
|
|
303
|
+
|
|
304
|
+
```text
|
|
305
|
+
I can render the following in the landing-page app:
|
|
306
|
+
|
|
307
|
+
1. Our Journey to Carbon Neutrality (Blog)
|
|
308
|
+
2. Building a Sustainable Supply Chain (Blog)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Message 2 (immediately after):** `ask_followup_question` with the question `"Render this content in the landing-page app?"` and options `Yes` / `No`. Note the question stays generic — it does NOT restate "these two items" or the count; that's exactly the kind of detail Message 1's list already carries, and stuffing it into the question text is the failure mode this pattern avoids (same reasoning as Step 4's search-confirmation message, which strips line breaks/formatting from anything placed inside `ask_followup_question`).
|
|
312
|
+
|
|
313
|
+
On `Yes`, dispatch delegation via `mcp__skill_bridge__load_skill("experience-cms-content-render")` — never the built-in Skill tool, which doesn't know about MCP-bridge skills and returns `Unknown skill`. Pass each selected item's `title`, content type FQN (e.g. `sfdc_cms__blog`), and its chosen entry's channel ID (`managedContentChannelDetails.id`, e.g. `0apVW000000TsHx`) **when it has one** — items 1 and 2 above each have exactly one entry, so their channel ID is included; item 5 has zero entries (empty `managedContentChannelDeliveryDetails`), so its channel ID is omitted entirely rather than read off a nonexistent entry. Also pass either its Unauthenticated URL (item 1/2 above) or, when absent, its `managedContentKey` renamed to `contentKey` (item 5 above) — the receiving skill's `SKILL.md` names this field `contentKey`. If the load call fails, tell the user the hand-off failed and stop — do not attempt the render yourself.
|