@salesforce/afv-skills 1.14.0 → 1.16.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/activating-datacloud/SKILL.md +0 -1
- package/skills/analyzing-omnistudio-dependencies/SKILL.md +0 -1
- package/skills/applying-slds/SKILL.md +322 -0
- package/skills/applying-slds/checklists.md +83 -0
- package/skills/applying-slds/examples.md +283 -0
- package/skills/applying-slds/guidance/README.md +83 -0
- package/skills/applying-slds/guidance/blueprints-index.md +213 -0
- package/skills/applying-slds/guidance/icons-guidance.md +186 -0
- package/skills/applying-slds/guidance/overviews/borders.md +236 -0
- package/skills/applying-slds/guidance/overviews/color.md +266 -0
- package/skills/applying-slds/guidance/overviews/display-density.md +366 -0
- package/skills/applying-slds/guidance/overviews/icons.md +240 -0
- package/skills/applying-slds/guidance/overviews/illustrations.md +235 -0
- package/skills/applying-slds/guidance/overviews/shadows.md +176 -0
- package/skills/applying-slds/guidance/overviews/spacing.md +216 -0
- package/skills/applying-slds/guidance/overviews/typography.md +323 -0
- package/skills/applying-slds/guidance/overviews/utilities.md +542 -0
- package/skills/applying-slds/guidance/slds-development-guide.md +288 -0
- package/skills/applying-slds/guidance/styling-hooks/borders.md +202 -0
- package/skills/applying-slds/guidance/styling-hooks/color/expressive-palette-hooks.md +153 -0
- package/skills/applying-slds/guidance/styling-hooks/color/index.md +171 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/accent-hooks.md +204 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/feedback-hooks.md +768 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/surface-hooks.md +337 -0
- package/skills/applying-slds/guidance/styling-hooks/color/system-hooks.md +132 -0
- package/skills/applying-slds/guidance/styling-hooks/index.md +327 -0
- package/skills/applying-slds/guidance/styling-hooks/shadows.md +238 -0
- package/skills/applying-slds/guidance/styling-hooks/spacing.md +254 -0
- package/skills/applying-slds/guidance/styling-hooks/typography.md +448 -0
- package/skills/applying-slds/guidance/utilities/alignment.md +119 -0
- package/skills/applying-slds/guidance/utilities/borders.md +131 -0
- package/skills/applying-slds/guidance/utilities/box.md +125 -0
- package/skills/applying-slds/guidance/utilities/color.md +165 -0
- package/skills/applying-slds/guidance/utilities/dark-mode.md +111 -0
- package/skills/applying-slds/guidance/utilities/description-list.md +168 -0
- package/skills/applying-slds/guidance/utilities/floats.md +117 -0
- package/skills/applying-slds/guidance/utilities/grid.md +264 -0
- package/skills/applying-slds/guidance/utilities/horizontal-list.md +110 -0
- package/skills/applying-slds/guidance/utilities/hyphenation.md +84 -0
- package/skills/applying-slds/guidance/utilities/index.md +205 -0
- package/skills/applying-slds/guidance/utilities/interactions.md +89 -0
- package/skills/applying-slds/guidance/utilities/layout.md +109 -0
- package/skills/applying-slds/guidance/utilities/line-clamp.md +131 -0
- package/skills/applying-slds/guidance/utilities/margin.md +155 -0
- package/skills/applying-slds/guidance/utilities/media-object.md +161 -0
- package/skills/applying-slds/guidance/utilities/name-value-list.md +152 -0
- package/skills/applying-slds/guidance/utilities/padding.md +155 -0
- package/skills/applying-slds/guidance/utilities/position.md +177 -0
- package/skills/applying-slds/guidance/utilities/print.md +114 -0
- package/skills/applying-slds/guidance/utilities/scrollable.md +126 -0
- package/skills/applying-slds/guidance/utilities/sizing.md +190 -0
- package/skills/applying-slds/guidance/utilities/themes.md +121 -0
- package/skills/applying-slds/guidance/utilities/truncate.md +127 -0
- package/skills/applying-slds/guidance/utilities/typography.md +166 -0
- package/skills/applying-slds/guidance/utilities/vertical-list.md +166 -0
- package/skills/applying-slds/guidance/utilities/visibility.md +228 -0
- package/skills/applying-slds/metadata/README.md +84 -0
- package/skills/applying-slds/metadata/blueprints/components/accordion.yaml +304 -0
- package/skills/applying-slds/metadata/blueprints/components/activity-timeline.yaml +92 -0
- package/skills/applying-slds/metadata/blueprints/components/alert.yaml +103 -0
- package/skills/applying-slds/metadata/blueprints/components/app-launcher.yaml +94 -0
- package/skills/applying-slds/metadata/blueprints/components/avatar-group.yaml +81 -0
- package/skills/applying-slds/metadata/blueprints/components/avatar.yaml +97 -0
- package/skills/applying-slds/metadata/blueprints/components/badges.yaml +102 -0
- package/skills/applying-slds/metadata/blueprints/components/brand-band.yaml +198 -0
- package/skills/applying-slds/metadata/blueprints/components/breadcrumbs.yaml +95 -0
- package/skills/applying-slds/metadata/blueprints/components/builder-header.yaml +192 -0
- package/skills/applying-slds/metadata/blueprints/components/button-groups.yaml +82 -0
- package/skills/applying-slds/metadata/blueprints/components/button-icons.yaml +295 -0
- package/skills/applying-slds/metadata/blueprints/components/buttons.yaml +230 -0
- package/skills/applying-slds/metadata/blueprints/components/cards.yaml +124 -0
- package/skills/applying-slds/metadata/blueprints/components/carousel.yaml +140 -0
- package/skills/applying-slds/metadata/blueprints/components/chat.yaml +179 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-button-group.yaml +192 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-button.yaml +204 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-toggle.yaml +177 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox.yaml +108 -0
- package/skills/applying-slds/metadata/blueprints/components/color-picker.yaml +172 -0
- package/skills/applying-slds/metadata/blueprints/components/combobox.yaml +136 -0
- package/skills/applying-slds/metadata/blueprints/components/counter.yaml +147 -0
- package/skills/applying-slds/metadata/blueprints/components/data-tables.yaml +157 -0
- package/skills/applying-slds/metadata/blueprints/components/datepickers.yaml +130 -0
- package/skills/applying-slds/metadata/blueprints/components/datetime-picker.yaml +155 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-composer.yaml +201 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-form-footer.yaml +161 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-utility-bar.yaml +175 -0
- package/skills/applying-slds/metadata/blueprints/components/drop-zone.yaml +115 -0
- package/skills/applying-slds/metadata/blueprints/components/dueling-picklist.yaml +196 -0
- package/skills/applying-slds/metadata/blueprints/components/dynamic-icons.yaml +128 -0
- package/skills/applying-slds/metadata/blueprints/components/dynamic-menu.yaml +141 -0
- package/skills/applying-slds/metadata/blueprints/components/expandable-section.yaml +115 -0
- package/skills/applying-slds/metadata/blueprints/components/expression.yaml +143 -0
- package/skills/applying-slds/metadata/blueprints/components/feeds.yaml +125 -0
- package/skills/applying-slds/metadata/blueprints/components/file-selector.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/files.yaml +119 -0
- package/skills/applying-slds/metadata/blueprints/components/form-element.yaml +145 -0
- package/skills/applying-slds/metadata/blueprints/components/global-header.yaml +120 -0
- package/skills/applying-slds/metadata/blueprints/components/global-navigation.yaml +100 -0
- package/skills/applying-slds/metadata/blueprints/components/icons.yaml +138 -0
- package/skills/applying-slds/metadata/blueprints/components/illustration.yaml +205 -0
- package/skills/applying-slds/metadata/blueprints/components/input.yaml +151 -0
- package/skills/applying-slds/metadata/blueprints/components/list-builder.yaml +127 -0
- package/skills/applying-slds/metadata/blueprints/components/lookups.yaml +132 -0
- package/skills/applying-slds/metadata/blueprints/components/map.yaml +118 -0
- package/skills/applying-slds/metadata/blueprints/components/menus.yaml +134 -0
- package/skills/applying-slds/metadata/blueprints/components/modals.yaml +152 -0
- package/skills/applying-slds/metadata/blueprints/components/notifications.yaml +88 -0
- package/skills/applying-slds/metadata/blueprints/components/page-headers.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/panels.yaml +149 -0
- package/skills/applying-slds/metadata/blueprints/components/path.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/picklist.yaml +125 -0
- package/skills/applying-slds/metadata/blueprints/components/pills.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/popovers.yaml +120 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-bar.yaml +110 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-indicator.yaml +133 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-ring.yaml +102 -0
- package/skills/applying-slds/metadata/blueprints/components/prompt.yaml +126 -0
- package/skills/applying-slds/metadata/blueprints/components/publishers.yaml +178 -0
- package/skills/applying-slds/metadata/blueprints/components/radio-button-group.yaml +172 -0
- package/skills/applying-slds/metadata/blueprints/components/radio-group.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/rich-text-editor.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/scoped-notifications.yaml +188 -0
- package/skills/applying-slds/metadata/blueprints/components/scoped-tabs.yaml +97 -0
- package/skills/applying-slds/metadata/blueprints/components/select.yaml +127 -0
- package/skills/applying-slds/metadata/blueprints/components/setup-assistant.yaml +152 -0
- package/skills/applying-slds/metadata/blueprints/components/slider.yaml +111 -0
- package/skills/applying-slds/metadata/blueprints/components/spinners.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/split-view.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/summary-detail.yaml +103 -0
- package/skills/applying-slds/metadata/blueprints/components/tabs.yaml +138 -0
- package/skills/applying-slds/metadata/blueprints/components/textarea.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/tiles.yaml +108 -0
- package/skills/applying-slds/metadata/blueprints/components/timepicker.yaml +111 -0
- package/skills/applying-slds/metadata/blueprints/components/toast.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/tooltips.yaml +107 -0
- package/skills/applying-slds/metadata/blueprints/components/tree-grid.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/trees.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/trial-bar.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/vertical-navigation.yaml +130 -0
- package/skills/applying-slds/metadata/blueprints/components/vertical-tabs.yaml +140 -0
- package/skills/applying-slds/metadata/blueprints/components/visual-picker.yaml +150 -0
- package/skills/applying-slds/metadata/blueprints/components/welcome-mat.yaml +136 -0
- package/skills/applying-slds/metadata/hooks-index.json +6272 -0
- package/skills/applying-slds/metadata/icon-metadata.json +38466 -0
- package/skills/applying-slds/metadata/utilities-index.json +21912 -0
- package/skills/applying-slds/references/component-selection.md +112 -0
- package/skills/applying-slds/references/icons-decision-guide.md +124 -0
- package/skills/applying-slds/references/styling-decision-guide.md +228 -0
- package/skills/applying-slds/references/utilities-quick-ref.md +125 -0
- package/skills/applying-slds/scripts/search-blueprints.cjs +117 -0
- package/skills/applying-slds/scripts/search-hooks.cjs +139 -0
- package/skills/applying-slds/scripts/search-icons.cjs +174 -0
- package/skills/applying-slds/scripts/search-utilities.cjs +161 -0
- package/skills/building-mobile-apps/SKILL.md +0 -1
- package/skills/building-omnistudio-callable-apex/SKILL.md +0 -1
- package/skills/building-omnistudio-datamapper/SKILL.md +0 -1
- package/skills/building-omnistudio-flexcard/SKILL.md +0 -1
- package/skills/building-omnistudio-integration-procedure/SKILL.md +0 -1
- package/skills/building-omnistudio-omniscript/SKILL.md +0 -1
- package/skills/building-sf-integrations/SKILL.md +0 -1
- package/skills/configuring-connected-apps/SKILL.md +0 -1
- package/skills/connecting-datacloud/SKILL.md +0 -1
- package/skills/creating-b2b-commerce-store/SKILL.md +0 -1
- package/skills/debugging-apex-logs/SKILL.md +0 -1
- package/skills/deploying-metadata/SKILL.md +0 -1
- package/skills/deploying-omnistudio-datapacks/SKILL.md +0 -1
- package/skills/developing-agentforce/SKILL.md +0 -1
- package/skills/fetching-salesforce-docs/SKILL.md +0 -1
- package/skills/generating-custom-lightning-type/SKILL.md +17 -39
- package/skills/generating-custom-lightning-type/assets/primitive-types-and-constraints.md +41 -0
- package/skills/generating-custom-lightning-type/references/widget-rendition.md +124 -0
- package/skills/generating-lwc-components/SKILL.md +0 -1
- package/skills/generating-mermaid-diagrams/SKILL.md +0 -1
- package/skills/generating-visual-diagrams/SKILL.md +0 -1
- package/skills/handling-sf-data/SKILL.md +0 -1
- package/skills/harmonizing-datacloud/SKILL.md +0 -1
- package/skills/integrating-b2b-commerce-open-code-components/SKILL.md +0 -1
- package/skills/investigating-agentforce-architecture/README.md +156 -0
- package/skills/investigating-agentforce-architecture/SKILL.md +230 -0
- package/skills/investigating-agentforce-architecture/assets/cli/describe_sobject.yaml +16 -0
- package/skills/investigating-agentforce-architecture/assets/cli/describe_tooling_sobject.yaml +17 -0
- package/skills/investigating-agentforce-architecture/assets/cli/list_metadata_genaiprompttemplate.yaml +17 -0
- package/skills/investigating-agentforce-architecture/assets/cli/org_display.yaml +15 -0
- package/skills/investigating-agentforce-architecture/assets/cli/retrieve_genai_plugin.yaml +18 -0
- package/skills/investigating-agentforce-architecture/assets/cli/show_access_token.yaml +27 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/action_tree.mmd +20 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/data_flow.mmd +19 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/dependency_graph.mmd +19 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/invocation_sequence.mmd +20 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/planner_state.mmd +18 -0
- package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_names.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/bot_definition_details.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/bot_version_lookup.soql +4 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_by_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_ids_by_names.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_view_by_durable_ids.soql +4 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_metadata_by_id.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/functions_by_plugins.soql +5 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_attrs_by_parent_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_bundle_functions.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_definition_by_agent_chain.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugin_functions_by_plugin_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugin_instructions_by_plugin_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugins_by_planner.soql +4 -0
- package/skills/investigating-agentforce-architecture/references/architecture_sections.md +243 -0
- package/skills/investigating-agentforce-architecture/references/contract.json +244 -0
- package/skills/investigating-agentforce-architecture/references/soql_fields.md +512 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/__init__.py +1 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/fs_guard.py +329 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/paths.py +110 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/runtime.py +59 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/sql.py +10 -0
- package/skills/investigating-agentforce-architecture/scripts/cache_check.py +234 -0
- package/skills/investigating-agentforce-architecture/scripts/config.py +131 -0
- package/skills/investigating-agentforce-architecture/scripts/fetch_soql.py +689 -0
- package/skills/investigating-agentforce-architecture/scripts/finalize.py +295 -0
- package/skills/investigating-agentforce-architecture/scripts/main.py +2835 -0
- package/skills/investigating-agentforce-architecture/scripts/metadata_listing.py +265 -0
- package/skills/investigating-agentforce-architecture/scripts/parallel_retrieve.py +69 -0
- package/skills/investigating-agentforce-architecture/scripts/parse_bundle.py +215 -0
- package/skills/investigating-agentforce-architecture/scripts/parse_wave.py +845 -0
- package/skills/investigating-agentforce-architecture/scripts/probe_channels.py +302 -0
- package/skills/investigating-agentforce-architecture/scripts/render_architecture.py +1043 -0
- package/skills/investigating-agentforce-architecture/scripts/resolve_bot.py +255 -0
- package/skills/investigating-agentforce-architecture/scripts/resolve_invocation_target.py +130 -0
- package/skills/investigating-agentforce-architecture/scripts/rest_client.py +763 -0
- package/skills/investigating-agentforce-architecture/scripts/retrieve_planner.py +13 -0
- package/skills/investigating-agentforce-architecture/scripts/sf_cli.py +242 -0
- package/skills/investigating-agentforce-architecture/scripts/soql_loader.py +253 -0
- package/skills/investigating-agentforce-architecture/scripts/summarize_tree.py +143 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/__init__.py +0 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/_bootstrap.py +23 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/__init__.py +0 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/genai_payloads.py +400 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check.py +307 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check_main.py +283 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_config.py +115 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_end_to_end_fixture.py +651 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_finalize.py +278 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_flow_children_inflation.py +582 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_fs_guard.py +113 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_iterative_wave_b.py +478 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_main_pipeline.py +3359 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parallel_retrieve.py +131 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_bundle.py +400 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave.py +644 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py +224 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_helpers.py +380 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_main.py +397 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_per_branch_visited.py +244 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_channels.py +359 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_cli_recipes.py +185 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py +810 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_bot.py +203 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_creds.py +157 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_invocation_target.py +145 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_rest_client.py +1253 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_runtime_override.py +100 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_sf_cli.py +261 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_signature_stamping.py +466 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_soql_loader.py +501 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_summarize_tree.py +241 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_write_emit_ctx.py +480 -0
- package/skills/investigating-agentforce-architecture/tools/emit_env.py +157 -0
- package/skills/investigating-agentforce-architecture/tools/emit_result.py +262 -0
- package/skills/investigating-agentforce-architecture/tools/sanitize.py +33 -0
- package/skills/investigating-agentforce-architecture/tools/write_emit_ctx.py +332 -0
- package/skills/investigating-agentforce-d360/README.md +123 -0
- package/skills/investigating-agentforce-d360/SKILL.md +163 -0
- package/skills/investigating-agentforce-d360/assets/dc/app_generation.sql +51 -0
- package/skills/investigating-agentforce-d360/assets/dc/content_category.sql +44 -0
- package/skills/investigating-agentforce-d360/assets/dc/content_quality.sql +41 -0
- package/skills/investigating-agentforce-d360/assets/dc/discover_sessions.sql +36 -0
- package/skills/investigating-agentforce-d360/assets/dc/feedback.sql +47 -0
- package/skills/investigating-agentforce-d360/assets/dc/feedback_details.sql +38 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_records.sql +45 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_llm.sql +50 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_metadata.sql +44 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_tags.sql +42 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_requests.sql +89 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_responses.sql +43 -0
- package/skills/investigating-agentforce-d360/assets/dc/generations.sql +52 -0
- package/skills/investigating-agentforce-d360/assets/dc/interactions.sql +53 -0
- package/skills/investigating-agentforce-d360/assets/dc/messages.sql +53 -0
- package/skills/investigating-agentforce-d360/assets/dc/messaging_session.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/moment_interactions.sql +34 -0
- package/skills/investigating-agentforce-d360/assets/dc/moments.sql +39 -0
- package/skills/investigating-agentforce-d360/assets/dc/participants.sql +48 -0
- package/skills/investigating-agentforce-d360/assets/dc/sessions.sql +78 -0
- package/skills/investigating-agentforce-d360/assets/dc/steps.sql +64 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_associations.sql +46 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_definition_associations.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_definitions.sql +50 -0
- package/skills/investigating-agentforce-d360/assets/dc/tags.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/telemetry_spans.sql +55 -0
- package/skills/investigating-agentforce-d360/references/artifacts.md +50 -0
- package/skills/investigating-agentforce-d360/references/dc_dmo_fields.md +823 -0
- package/skills/investigating-agentforce-d360/references/dc_pipeline_contract.md +608 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/__init__.py +2 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/cli_override.py +98 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/fs_guard.py +334 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/paths.py +155 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/runtime.py +59 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/sql.py +14 -0
- package/skills/investigating-agentforce-d360/scripts/assemble_dc.py +1624 -0
- package/skills/investigating-agentforce-d360/scripts/config.py +45 -0
- package/skills/investigating-agentforce-d360/scripts/dc.py +188 -0
- package/skills/investigating-agentforce-d360/scripts/discover_sessions.py +556 -0
- package/skills/investigating-agentforce-d360/scripts/fetch_dc.py +1045 -0
- package/skills/investigating-agentforce-d360/scripts/render_dc.py +1750 -0
- package/skills/investigating-agentforce-d360/scripts/resolve_session.py +264 -0
- package/skills/investigating-agentforce-d360/scripts/storage.py +92 -0
- package/skills/investigating-agentforce-d360/scripts/tests/__init__.py +0 -0
- package/skills/investigating-agentforce-d360/scripts/tests/_bootstrap.py +15 -0
- package/skills/investigating-agentforce-d360/scripts/tests/fixtures/__init__.py +0 -0
- package/skills/investigating-agentforce-d360/scripts/tests/fixtures/synthetic_session.py +424 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_bootstrap_and_mode.py +115 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct.py +220 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct_integration.py +158 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_helpers.py +287 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_integration.py +247 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_dc_and_resolve_session.py +433 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions.py +458 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions_grep_ci.py +193 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_helpers.py +266 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_identity.py +528 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_main.py +251 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall.py +229 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall_full.py +283 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_identity_coherence.py +327 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_branches.py +256 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_gateway_direct.py +130 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_helpers.py +291 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_integration.py +220 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_planner_llm_calls.py +284 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_show_prompts_gating.py +215 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_from_disk.py +100 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_session_main.py +149 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_runtime_override.py +104 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape.py +95 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape_dropped_by_stdm.py +85 -0
- package/skills/managing-managed-event-subscription/SKILL.md +152 -0
- package/skills/managing-managed-event-subscription/assets/managed-event-subscription-template.xml +20 -0
- package/skills/managing-managed-event-subscription/references/delete-guide.md +57 -0
- package/skills/managing-managed-event-subscription/references/topic-name-formats.md +26 -0
- package/skills/managing-managed-event-subscription/references/update-constraints.md +30 -0
- package/skills/modeling-omnistudio-epc-catalog/SKILL.md +0 -1
- package/skills/observing-agentforce/SKILL.md +0 -1
- package/skills/orchestrating-datacloud/SKILL.md +0 -1
- package/skills/preparing-datacloud/SKILL.md +0 -1
- package/skills/querying-soql/SKILL.md +0 -1
- package/skills/retrieving-datacloud/SKILL.md +0 -1
- package/skills/running-apex-tests/SKILL.md +0 -1
- package/skills/running-code-analyzer/SKILL.md +0 -1
- package/skills/segmenting-datacloud/SKILL.md +0 -1
- package/skills/testing-agentforce/SKILL.md +0 -1
- package/skills/uplifting-components-to-slds2/SKILL.md +3 -2
- package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +30 -9
- package/skills/uplifting-components-to-slds2/references/examples.md +24 -6
- package/skills/validating-slds/SKILL.md +262 -0
- package/skills/validating-slds/references/quality-checks.md +308 -0
- package/skills/validating-slds/references/report-format.md +302 -0
- package/skills/validating-slds/scripts/analyze-quality.cjs +521 -0
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
"""Session discovery for investigating-agentforce-d360 — Data Cloud only.
|
|
2
|
+
|
|
3
|
+
Lets the user find candidate sessions when they don't have a session id.
|
|
4
|
+
Output is a numbered picker table to stdout; the skill agent asks the user
|
|
5
|
+
to pick one, then falls through to the existing fetch_dc.py pipeline with
|
|
6
|
+
the chosen UUID. Writes no artifacts.
|
|
7
|
+
|
|
8
|
+
DC-only by design (no OTel path).
|
|
9
|
+
|
|
10
|
+
## CLI
|
|
11
|
+
|
|
12
|
+
python3 scripts/discover_sessions.py --org <alias> [options]
|
|
13
|
+
|
|
14
|
+
Options (all optional except --org):
|
|
15
|
+
--since <expr> "last 2 hours" | "last 10" | "today" | "yesterday"
|
|
16
|
+
| "YYYY-MM-DD" | "YYYY-MM-DD to YYYY-MM-DD"
|
|
17
|
+
| explicit UTC/TZ datetime (RFC 3339). Default: last 24 hours.
|
|
18
|
+
--agent <name> Agent API name (e.g. MyAgent). Adds participant JOIN.
|
|
19
|
+
--channel <type> Builder | Messaging | Voice. "Messaging" maps to
|
|
20
|
+
"SCRT2 - EmbeddedMessaging" in SQL.
|
|
21
|
+
--outcome <type> USER_ENDED | ESCALATED | TRANSFERRED | TIMEOUT | NOT_SET
|
|
22
|
+
--grep <pattern> Substring in conversation text (user input + agent output).
|
|
23
|
+
Adds interaction + message JOINs. Case-insensitive by
|
|
24
|
+
default (LOWER on both sides); pass --grep-case-sensitive
|
|
25
|
+
for exact match. `%` and `_` are escaped so they match
|
|
26
|
+
the literal char.
|
|
27
|
+
--grep-case-sensitive
|
|
28
|
+
Opt-in: make --grep case-sensitive (rare; default is
|
|
29
|
+
case-insensitive because users almost always type the
|
|
30
|
+
quote in whatever case they remember it).
|
|
31
|
+
--tz <IANA> Override auto-detected local tz (e.g. America/Los_Angeles).
|
|
32
|
+
Only affects calendar-day resolution (today/yesterday/
|
|
33
|
+
bare-date/date-range); `last N` windows are anchored to
|
|
34
|
+
UTC regardless. The chosen tz is echoed in zero-row
|
|
35
|
+
output so the user can sanity-check boundaries.
|
|
36
|
+
--limit <N> Max sessions. Default 20.
|
|
37
|
+
|
|
38
|
+
Exit codes:
|
|
39
|
+
0 — ≥1 session found
|
|
40
|
+
2 — zero sessions (picker prints composed SQL so user can widen)
|
|
41
|
+
other — DC query or arg-parse error
|
|
42
|
+
"""
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
import argparse
|
|
46
|
+
import html
|
|
47
|
+
import os
|
|
48
|
+
import re
|
|
49
|
+
import sys
|
|
50
|
+
from dataclasses import dataclass
|
|
51
|
+
from datetime import datetime, timedelta, timezone
|
|
52
|
+
from pathlib import Path
|
|
53
|
+
from typing import Optional
|
|
54
|
+
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
|
55
|
+
|
|
56
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
57
|
+
|
|
58
|
+
from dc import DCQueryError, load_sql, post, resolve_org
|
|
59
|
+
from _shared.sql import _escape_sql_literal
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ---- channel alias ---------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
# Source-skill convention: users type "Messaging" but DC stores the full
|
|
65
|
+
# SCRT2 string. Other channels pass through as-is (Builder, Voice).
|
|
66
|
+
_CHANNEL_ALIAS = {
|
|
67
|
+
"Messaging": "SCRT2 - EmbeddedMessaging",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ---- escaping --------------------------------------------------------------
|
|
72
|
+
#
|
|
73
|
+
# `_escape_sql_literal` is imported from `_shared.sql` (single source of
|
|
74
|
+
# truth per its module docstring). Do NOT re-declare locally — divergence
|
|
75
|
+
# would break the documented contract that every DC-SQL caller shares one
|
|
76
|
+
# escape strategy.
|
|
77
|
+
|
|
78
|
+
_LIKE_ESCAPE = "!" # ASCII, doesn't collide with SQL syntax, no quoting ambiguity
|
|
79
|
+
|
|
80
|
+
def _escape_like_pattern(s: str) -> str:
|
|
81
|
+
"""Escape SQL LIKE wildcards (`%`, `_`) and the escape char itself
|
|
82
|
+
(`!`) so `--grep "100%_off"` matches the literal text, not "100
|
|
83
|
+
anything-underscore-off". Quote-doubling still happens on top of this
|
|
84
|
+
via _escape_sql_literal. Used with the `ESCAPE '!'` clause in the
|
|
85
|
+
composed SQL.
|
|
86
|
+
|
|
87
|
+
Backslash was avoided because `ESCAPE '\\'` in source reduces to a
|
|
88
|
+
bare backslash on the wire, which some SQL parsers read as an
|
|
89
|
+
unterminated string literal (\\' → escaped quote).
|
|
90
|
+
"""
|
|
91
|
+
return (
|
|
92
|
+
s.replace(_LIKE_ESCAPE, _LIKE_ESCAPE * 2)
|
|
93
|
+
.replace("%", f"{_LIKE_ESCAPE}%")
|
|
94
|
+
.replace("_", f"{_LIKE_ESCAPE}_")
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ---- time parsing ----------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
@dataclass(frozen=True)
|
|
101
|
+
class TimeRange:
|
|
102
|
+
"""Half-open UTC range [start, end). `expr` is the original user input
|
|
103
|
+
so the picker header can echo back what was searched."""
|
|
104
|
+
start_utc: datetime
|
|
105
|
+
end_utc: datetime
|
|
106
|
+
expr: str
|
|
107
|
+
tz_name: str # IANA or "UTC"
|
|
108
|
+
|
|
109
|
+
def iso(self, dt: datetime) -> str:
|
|
110
|
+
return dt.strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _autodetect_tz() -> str:
|
|
114
|
+
"""IANA tz from /etc/localtime symlink (macOS / most Linux). Falls
|
|
115
|
+
back to the TZ env var, then UTC."""
|
|
116
|
+
link = Path("/etc/localtime")
|
|
117
|
+
try:
|
|
118
|
+
if link.is_symlink():
|
|
119
|
+
target = os.readlink(link)
|
|
120
|
+
# Typically .../zoneinfo/America/Los_Angeles
|
|
121
|
+
if "zoneinfo/" in target:
|
|
122
|
+
return target.split("zoneinfo/", 1)[1]
|
|
123
|
+
except OSError:
|
|
124
|
+
pass
|
|
125
|
+
return os.environ.get("TZ") or "UTC"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
_RX_DATE = re.compile(r"^\d{4}-\d{2}-\d{2}$") # @rule-suppress starter-sec-002 — re.compile, not eval/exec
|
|
129
|
+
_RX_DATE_RANGE = re.compile(r"^(\d{4}-\d{2}-\d{2})\s+to\s+(\d{4}-\d{2}-\d{2})$") # @rule-suppress starter-sec-002 — re.compile, not eval/exec
|
|
130
|
+
_RX_LAST_N = re.compile( # @rule-suppress starter-sec-002 — re.compile, not eval/exec
|
|
131
|
+
r"^last\s+(\d+)(?:\s+(hour|hours|day|days|minute|minutes))?$",
|
|
132
|
+
re.IGNORECASE,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def parse_time_expr(expr: Optional[str], tz_name: str) -> TimeRange:
|
|
137
|
+
"""Parse one of the supported time expressions into a UTC half-open
|
|
138
|
+
range. Default (None) is "last 24 hours" so every query has a sane
|
|
139
|
+
time predicate — DC scans over the whole DMO without one are slow."""
|
|
140
|
+
tz_name = tz_name or _autodetect_tz()
|
|
141
|
+
try:
|
|
142
|
+
local_tz = ZoneInfo(tz_name)
|
|
143
|
+
except ZoneInfoNotFoundError:
|
|
144
|
+
raise SystemExit(f"discover_sessions: unknown IANA timezone {tz_name!r}")
|
|
145
|
+
|
|
146
|
+
now_utc = datetime.now(timezone.utc)
|
|
147
|
+
|
|
148
|
+
if expr is None:
|
|
149
|
+
return TimeRange(
|
|
150
|
+
start_utc=now_utc - timedelta(hours=24),
|
|
151
|
+
end_utc=now_utc,
|
|
152
|
+
expr="last 24 hours (default)",
|
|
153
|
+
tz_name=tz_name,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
s = expr.strip()
|
|
157
|
+
sl = s.lower()
|
|
158
|
+
|
|
159
|
+
# "last N" / "last N hours" / "last N days" / "last N minutes"
|
|
160
|
+
m = _RX_LAST_N.match(sl)
|
|
161
|
+
if m:
|
|
162
|
+
n = int(m.group(1))
|
|
163
|
+
unit = (m.group(2) or "hours").lower()
|
|
164
|
+
if unit in ("hour", "hours"):
|
|
165
|
+
delta = timedelta(hours=n)
|
|
166
|
+
elif unit in ("day", "days"):
|
|
167
|
+
delta = timedelta(days=n)
|
|
168
|
+
elif unit in ("minute", "minutes"):
|
|
169
|
+
delta = timedelta(minutes=n)
|
|
170
|
+
else:
|
|
171
|
+
raise SystemExit(f"discover_sessions: unsupported time unit {unit!r}")
|
|
172
|
+
return TimeRange(
|
|
173
|
+
start_utc=now_utc - delta,
|
|
174
|
+
end_utc=now_utc,
|
|
175
|
+
expr=s,
|
|
176
|
+
tz_name=tz_name,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# "today" / "yesterday" — calendar day in local tz
|
|
180
|
+
if sl in ("today", "yesterday"):
|
|
181
|
+
local_now = datetime.now(local_tz)
|
|
182
|
+
offset_days = 0 if sl == "today" else 1
|
|
183
|
+
start_local = local_now.replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=offset_days)
|
|
184
|
+
end_local = start_local + timedelta(days=1)
|
|
185
|
+
return TimeRange(
|
|
186
|
+
start_utc=start_local.astimezone(timezone.utc),
|
|
187
|
+
end_utc=end_local.astimezone(timezone.utc),
|
|
188
|
+
expr=s,
|
|
189
|
+
tz_name=tz_name,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# "YYYY-MM-DD to YYYY-MM-DD" — inclusive range in local tz
|
|
193
|
+
m = _RX_DATE_RANGE.match(s)
|
|
194
|
+
if m:
|
|
195
|
+
start_d, end_d = m.group(1), m.group(2)
|
|
196
|
+
start_local = datetime.strptime(start_d, "%Y-%m-%d").replace(tzinfo=local_tz)
|
|
197
|
+
end_local = datetime.strptime(end_d, "%Y-%m-%d").replace(tzinfo=local_tz) + timedelta(days=1)
|
|
198
|
+
return TimeRange(
|
|
199
|
+
start_utc=start_local.astimezone(timezone.utc),
|
|
200
|
+
end_utc=end_local.astimezone(timezone.utc),
|
|
201
|
+
expr=s,
|
|
202
|
+
tz_name=tz_name,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Bare "YYYY-MM-DD"
|
|
206
|
+
if _RX_DATE.match(s):
|
|
207
|
+
start_local = datetime.strptime(s, "%Y-%m-%d").replace(tzinfo=local_tz)
|
|
208
|
+
end_local = start_local + timedelta(days=1)
|
|
209
|
+
return TimeRange(
|
|
210
|
+
start_utc=start_local.astimezone(timezone.utc),
|
|
211
|
+
end_utc=end_local.astimezone(timezone.utc),
|
|
212
|
+
expr=s,
|
|
213
|
+
tz_name=tz_name,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Try RFC 3339 single datetime — caller probably meant "since this time"
|
|
217
|
+
try:
|
|
218
|
+
dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
|
|
219
|
+
if dt.tzinfo is None:
|
|
220
|
+
dt = dt.replace(tzinfo=local_tz)
|
|
221
|
+
return TimeRange(
|
|
222
|
+
start_utc=dt.astimezone(timezone.utc),
|
|
223
|
+
end_utc=now_utc,
|
|
224
|
+
expr=f"since {s}",
|
|
225
|
+
tz_name=tz_name,
|
|
226
|
+
)
|
|
227
|
+
except ValueError:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
raise SystemExit(
|
|
231
|
+
f"discover_sessions: cannot parse --since {s!r}. "
|
|
232
|
+
f"Try: `last 2 hours`, `today`, `yesterday`, `2026-04-22`, "
|
|
233
|
+
f"`2026-04-22 to 2026-04-25`, or an ISO datetime."
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# ---- SQL composition -------------------------------------------------------
|
|
238
|
+
|
|
239
|
+
def compose_sql(
|
|
240
|
+
*,
|
|
241
|
+
tr: TimeRange,
|
|
242
|
+
agent: Optional[str],
|
|
243
|
+
channel: Optional[str],
|
|
244
|
+
outcome: Optional[str],
|
|
245
|
+
grep: Optional[str],
|
|
246
|
+
grep_case_sensitive: bool = False,
|
|
247
|
+
limit: int,
|
|
248
|
+
) -> str:
|
|
249
|
+
"""Build the discovery SQL. Conditional JOINs + WHERE ANDs are
|
|
250
|
+
composed from `tr` + flag args; the SQL skeleton lives at
|
|
251
|
+
`assets/dc/discover_sessions.sql`."""
|
|
252
|
+
joins: list[str] = []
|
|
253
|
+
wheres: list[str] = [
|
|
254
|
+
f"s.ssot__StartTimestamp__c >= '{tr.iso(tr.start_utc)}'",
|
|
255
|
+
f"s.ssot__StartTimestamp__c < '{tr.iso(tr.end_utc)}'",
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
if agent:
|
|
259
|
+
joins.append(
|
|
260
|
+
"JOIN ssot__AiAgentSessionParticipant__dlm p "
|
|
261
|
+
"ON s.ssot__Id__c = p.ssot__AiAgentSessionId__c"
|
|
262
|
+
)
|
|
263
|
+
wheres.append(f"p.ssot__AiAgentApiName__c = '{_escape_sql_literal(agent)}'")
|
|
264
|
+
|
|
265
|
+
if grep:
|
|
266
|
+
joins.append(
|
|
267
|
+
"JOIN ssot__AiAgentInteraction__dlm i "
|
|
268
|
+
"ON s.ssot__Id__c = i.ssot__AiAgentSessionId__c"
|
|
269
|
+
)
|
|
270
|
+
joins.append(
|
|
271
|
+
"JOIN ssot__AiAgentInteractionMessage__dlm m "
|
|
272
|
+
"ON i.ssot__Id__c = m.ssot__AiAgentInteractionId__c"
|
|
273
|
+
)
|
|
274
|
+
# LIKE wildcards (%, _) and the escape char (!) are neutralized so
|
|
275
|
+
# `--grep "100%_off"` matches the literal substring. Quote-doubling
|
|
276
|
+
# is layered on top via _escape_sql_literal. The `ESCAPE '!'` clause
|
|
277
|
+
# tells SQL the escape char is `!`.
|
|
278
|
+
pat = _escape_sql_literal(_escape_like_pattern(grep))
|
|
279
|
+
if grep_case_sensitive:
|
|
280
|
+
wheres.append(
|
|
281
|
+
f"m.ssot__ContentText__c LIKE '%{pat}%' ESCAPE '{_LIKE_ESCAPE}'"
|
|
282
|
+
)
|
|
283
|
+
else:
|
|
284
|
+
# Lowercase both sides so the predicate is case-insensitive.
|
|
285
|
+
# Default behaviour — users almost always type the quoted
|
|
286
|
+
# phrase in whatever case they remember, not the case the
|
|
287
|
+
# agent rendered it in.
|
|
288
|
+
wheres.append(
|
|
289
|
+
f"LOWER(m.ssot__ContentText__c) LIKE LOWER('%{pat}%') "
|
|
290
|
+
f"ESCAPE '{_LIKE_ESCAPE}'"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if channel:
|
|
294
|
+
ch = _CHANNEL_ALIAS.get(channel, channel)
|
|
295
|
+
wheres.append(f"s.ssot__AiAgentChannelType__c = '{_escape_sql_literal(ch)}'")
|
|
296
|
+
|
|
297
|
+
if outcome:
|
|
298
|
+
wheres.append(f"s.ssot__AiAgentSessionEndType__c = '{_escape_sql_literal(outcome)}'")
|
|
299
|
+
|
|
300
|
+
# DISTINCT when JOINs are present — a session can JOIN multiple
|
|
301
|
+
# participants / messages and explode rows.
|
|
302
|
+
columns = (
|
|
303
|
+
"s.ssot__Id__c, s.ssot__StartTimestamp__c, s.ssot__EndTimestamp__c, "
|
|
304
|
+
"s.ssot__AiAgentChannelType__c, s.ssot__AiAgentSessionEndType__c"
|
|
305
|
+
)
|
|
306
|
+
select_list = f"DISTINCT {columns}" if joins else columns
|
|
307
|
+
|
|
308
|
+
return load_sql(
|
|
309
|
+
"discover_sessions",
|
|
310
|
+
SELECT_LIST=select_list,
|
|
311
|
+
JOINS="\n".join(joins),
|
|
312
|
+
WHERE_CLAUSE=" AND ".join(wheres),
|
|
313
|
+
LIMIT=str(limit),
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# ---- agent-name enrichment -------------------------------------------------
|
|
318
|
+
|
|
319
|
+
_NAME_NOT_SET = {"", "NOT_SET", None}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def fetch_agent_names(
|
|
323
|
+
session_ids: list[str], *, instance_url: str, token: str
|
|
324
|
+
) -> dict[str, str]:
|
|
325
|
+
"""One follow-up query to grab agent names for discovered sessions.
|
|
326
|
+
The discovery query itself drops agent name from the projection
|
|
327
|
+
because adding it to SELECT forces a participant JOIN on every query
|
|
328
|
+
(including the no-filter case) and DC penalizes that.
|
|
329
|
+
|
|
330
|
+
Returns {session_uuid: agent_api_name}. Missing sessions → not in dict.
|
|
331
|
+
|
|
332
|
+
No role filter on the SQL. Some agent shapes (e.g. MyAgent) leave
|
|
333
|
+
AGENT-role rows with ``ssot__AiAgentApiName__c = 'NOT_SET'`` while USER-
|
|
334
|
+
role rows correctly carry the agent's api_name. Filtering to ``role =
|
|
335
|
+
'AGENT'`` would either return no row (picker shows '—' for a session
|
|
336
|
+
that clearly has an agent) or return the literal ``'NOT_SET'`` (picker
|
|
337
|
+
misleads the operator). Dropping the role filter and de-duplicating in
|
|
338
|
+
Python with the dominant-agent rule (``sorted(...)[0]``) recovers both
|
|
339
|
+
cases. Mirrors the fallback in ``fetch_dc._resolve_identity``.
|
|
340
|
+
"""
|
|
341
|
+
if not session_ids:
|
|
342
|
+
return {}
|
|
343
|
+
quoted = ", ".join(f"'{_escape_sql_literal(sid)}'" for sid in session_ids)
|
|
344
|
+
sql = (
|
|
345
|
+
"SELECT ssot__AiAgentSessionId__c, ssot__AiAgentApiName__c "
|
|
346
|
+
"FROM ssot__AiAgentSessionParticipant__dlm "
|
|
347
|
+
f"WHERE ssot__AiAgentSessionId__c IN ({quoted})"
|
|
348
|
+
)
|
|
349
|
+
try:
|
|
350
|
+
rows = post(sql, instance_url, token, "discover_sessions_agent_names")
|
|
351
|
+
except DCQueryError:
|
|
352
|
+
return {} # non-fatal — picker shows "—" for agent
|
|
353
|
+
# Group all api_names per session, drop NOT_SET sentinels, dominant-agent
|
|
354
|
+
# pick (lexicographic first) to match the rule used in fetch_dc.
|
|
355
|
+
# Sessions where every participant row is NOT_SET fall out of the result
|
|
356
|
+
# entirely — the picker will show '—', which is more honest than
|
|
357
|
+
# displaying the literal 'NOT_SET'.
|
|
358
|
+
by_sid: dict[str, set[str]] = {}
|
|
359
|
+
for row in rows:
|
|
360
|
+
sid = row.get("ssot__AiAgentSessionId__c")
|
|
361
|
+
name = row.get("ssot__AiAgentApiName__c")
|
|
362
|
+
if not sid or name in _NAME_NOT_SET:
|
|
363
|
+
continue
|
|
364
|
+
by_sid.setdefault(sid, set()).add(name)
|
|
365
|
+
return {sid: sorted(names)[0] for sid, names in by_sid.items() if names}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
# ---- rendering -------------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
def _fmt_duration(start_iso: Optional[str], end_iso: Optional[str]) -> str:
|
|
371
|
+
"""Human-readable duration (e.g. "1m 12s", "28s") from two ISO timestamps.
|
|
372
|
+
Returns "—" if start_iso is missing or unparseable. When end_iso is
|
|
373
|
+
None but start_iso parses, computes elapsed-from-now and appends "+"
|
|
374
|
+
to signal the session is still open (or end timestamp hasn't
|
|
375
|
+
materialized in DC yet)."""
|
|
376
|
+
if not start_iso:
|
|
377
|
+
return "—"
|
|
378
|
+
try:
|
|
379
|
+
start = datetime.fromisoformat(start_iso.replace("Z", "+00:00"))
|
|
380
|
+
except ValueError:
|
|
381
|
+
return "—"
|
|
382
|
+
ongoing = False
|
|
383
|
+
if not end_iso:
|
|
384
|
+
end = datetime.now(timezone.utc)
|
|
385
|
+
ongoing = True
|
|
386
|
+
else:
|
|
387
|
+
try:
|
|
388
|
+
end = datetime.fromisoformat(end_iso.replace("Z", "+00:00"))
|
|
389
|
+
except ValueError:
|
|
390
|
+
return "—"
|
|
391
|
+
seconds = int((end - start).total_seconds())
|
|
392
|
+
if seconds < 0:
|
|
393
|
+
return "—"
|
|
394
|
+
suffix = "+" if ongoing else ""
|
|
395
|
+
if seconds < 60:
|
|
396
|
+
return f"{seconds}s{suffix}"
|
|
397
|
+
m, s = divmod(seconds, 60)
|
|
398
|
+
if m < 60:
|
|
399
|
+
return (f"{m}m {s}s" if s else f"{m}m") + suffix
|
|
400
|
+
h, m = divmod(m, 60)
|
|
401
|
+
return f"{h}h {m}m{suffix}"
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _fmt_channel(raw: Optional[str]) -> str:
|
|
405
|
+
"""Compact channel label for the picker."""
|
|
406
|
+
if not raw:
|
|
407
|
+
return "—"
|
|
408
|
+
# Reverse the Messaging alias for readability.
|
|
409
|
+
if raw == "SCRT2 - EmbeddedMessaging":
|
|
410
|
+
return "Messaging"
|
|
411
|
+
# Custom channel labels arrive HTML-encoded from DC (e.g. "E & O").
|
|
412
|
+
return html.unescape(raw)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _fmt_start(iso: Optional[str]) -> str:
|
|
416
|
+
if not iso:
|
|
417
|
+
return "—"
|
|
418
|
+
try:
|
|
419
|
+
dt = datetime.fromisoformat(iso.replace("Z", "+00:00"))
|
|
420
|
+
except ValueError:
|
|
421
|
+
return iso
|
|
422
|
+
return dt.strftime("%Y-%m-%d %H:%M:%SZ")
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def render_picker(
|
|
426
|
+
*,
|
|
427
|
+
rows: list[dict],
|
|
428
|
+
agent_by_sid: dict[str, str],
|
|
429
|
+
org: str,
|
|
430
|
+
tr: TimeRange,
|
|
431
|
+
filters: dict[str, Optional[str]],
|
|
432
|
+
composed_sql: str,
|
|
433
|
+
) -> str:
|
|
434
|
+
"""Markdown picker. Returned as a string so the caller prints once
|
|
435
|
+
(single subprocess write on stdout)."""
|
|
436
|
+
filter_bits = [f"{tr.expr}"]
|
|
437
|
+
for k, v in filters.items():
|
|
438
|
+
if v:
|
|
439
|
+
filter_bits.append(f"{k}={v}")
|
|
440
|
+
header_filters = " · ".join(filter_bits)
|
|
441
|
+
|
|
442
|
+
if not rows:
|
|
443
|
+
# Echo the composed SQL so the user can widen the query.
|
|
444
|
+
return (
|
|
445
|
+
f"No sessions matched in **{org}** ({header_filters}).\n\n"
|
|
446
|
+
f"Range searched (UTC): `{tr.iso(tr.start_utc)}` → `{tr.iso(tr.end_utc)}`\n"
|
|
447
|
+
f"Local tz: `{tr.tz_name}`\n\n"
|
|
448
|
+
f"Composed SQL:\n```sql\n{composed_sql}\n```\n"
|
|
449
|
+
f"Try widening: larger `--since`, drop `--channel`/`--outcome`, or broaden `--grep`."
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
lines = [
|
|
453
|
+
f"Found **{len(rows)}** session{'s' if len(rows) != 1 else ''} in "
|
|
454
|
+
f"**{org}** ({header_filters}):",
|
|
455
|
+
"",
|
|
456
|
+
"| # | UUID | Start (UTC) | Agent | Channel | Duration | Outcome |",
|
|
457
|
+
"|---|------|-------------|-------|---------|----------|---------|",
|
|
458
|
+
]
|
|
459
|
+
for i, row in enumerate(rows, start=1):
|
|
460
|
+
uuid = row.get("ssot__Id__c") or "—"
|
|
461
|
+
agent = agent_by_sid.get(uuid, "—")
|
|
462
|
+
lines.append(
|
|
463
|
+
f"| {i} | `{uuid}` | {_fmt_start(row.get('ssot__StartTimestamp__c'))} "
|
|
464
|
+
f"| {agent} | {_fmt_channel(row.get('ssot__AiAgentChannelType__c'))} "
|
|
465
|
+
f"| {_fmt_duration(row.get('ssot__StartTimestamp__c'), row.get('ssot__EndTimestamp__c'))} "
|
|
466
|
+
f"| {row.get('ssot__AiAgentSessionEndType__c') or '—'} |"
|
|
467
|
+
)
|
|
468
|
+
lines.append("")
|
|
469
|
+
lines.append(f"Pick a number (1–{len(rows)}) to trace, or paste a UUID.")
|
|
470
|
+
return "\n".join(lines)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
# ---- CLI -------------------------------------------------------------------
|
|
474
|
+
|
|
475
|
+
def main() -> int:
|
|
476
|
+
ap = argparse.ArgumentParser(
|
|
477
|
+
description="Find candidate Agentforce sessions via Data Cloud. "
|
|
478
|
+
"Prints a numbered picker; no artifacts written.",
|
|
479
|
+
)
|
|
480
|
+
ap.add_argument("--org", required=True, help="sf CLI org alias (the one you configured via `sf org login`)")
|
|
481
|
+
ap.add_argument("--since", help='time range: "last 2 hours" | "today" | "yesterday" | '
|
|
482
|
+
'"YYYY-MM-DD" | "YYYY-MM-DD to YYYY-MM-DD" | ISO datetime. '
|
|
483
|
+
'Default: last 24 hours.')
|
|
484
|
+
ap.add_argument("--agent", help="Agent API name (exact match)")
|
|
485
|
+
ap.add_argument("--channel", help="Builder | Messaging | Voice (or full STDM string)")
|
|
486
|
+
ap.add_argument("--outcome", help="USER_ENDED | ESCALATED | TRANSFERRED | TIMEOUT | NOT_SET")
|
|
487
|
+
ap.add_argument(
|
|
488
|
+
"--grep",
|
|
489
|
+
help="substring in conversation text (user + agent); "
|
|
490
|
+
"case-insensitive by default — pass --grep-case-sensitive for exact match",
|
|
491
|
+
)
|
|
492
|
+
ap.add_argument(
|
|
493
|
+
"--grep-case-sensitive",
|
|
494
|
+
action="store_true",
|
|
495
|
+
default=False,
|
|
496
|
+
help="opt-in: make --grep case-sensitive (rare; default is case-insensitive)",
|
|
497
|
+
)
|
|
498
|
+
ap.add_argument("--tz", help="IANA timezone override for calendar-day resolution")
|
|
499
|
+
ap.add_argument("--limit", type=int, default=20, help="max sessions (default 20)")
|
|
500
|
+
# Runtime-agnostic path overrides; default to ~/.vibe/...
|
|
501
|
+
# discover_sessions doesn't read DATA_ROOT directly, but accepts the flags
|
|
502
|
+
# for contract consistency with the other 4 d360 entry scripts.
|
|
503
|
+
from _shared.cli_override import add_cli_flags, apply_overrides
|
|
504
|
+
add_cli_flags(ap)
|
|
505
|
+
args = ap.parse_args()
|
|
506
|
+
apply_overrides(args, caller_globals=globals())
|
|
507
|
+
|
|
508
|
+
if args.limit < 1:
|
|
509
|
+
raise SystemExit("discover_sessions: --limit must be >= 1")
|
|
510
|
+
|
|
511
|
+
tr = parse_time_expr(args.since, args.tz or "")
|
|
512
|
+
sql = compose_sql(
|
|
513
|
+
tr=tr,
|
|
514
|
+
agent=args.agent,
|
|
515
|
+
channel=args.channel,
|
|
516
|
+
outcome=args.outcome,
|
|
517
|
+
grep=args.grep,
|
|
518
|
+
grep_case_sensitive=args.grep_case_sensitive,
|
|
519
|
+
limit=args.limit,
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
instance_url, token = resolve_org(args.org)
|
|
523
|
+
try:
|
|
524
|
+
rows = post(sql, instance_url, token, "discover_sessions")
|
|
525
|
+
except DCQueryError as e:
|
|
526
|
+
print(f"discover_sessions: DC query failed.\n{e}", file=sys.stderr)
|
|
527
|
+
return 1
|
|
528
|
+
|
|
529
|
+
agent_by_sid: dict[str, str] = {}
|
|
530
|
+
if rows:
|
|
531
|
+
sids = [r["ssot__Id__c"] for r in rows if r.get("ssot__Id__c")]
|
|
532
|
+
agent_by_sid = fetch_agent_names(sids, instance_url=instance_url, token=token)
|
|
533
|
+
|
|
534
|
+
filters = {
|
|
535
|
+
"agent": args.agent,
|
|
536
|
+
"channel": args.channel,
|
|
537
|
+
"outcome": args.outcome,
|
|
538
|
+
"grep": args.grep,
|
|
539
|
+
# Only surface the case-sensitive note when --grep is in play AND
|
|
540
|
+
# the user opted into exact match — the default (insensitive) is
|
|
541
|
+
# the boring path and shouldn't clutter the picker header.
|
|
542
|
+
"grep_case_sensitive": "yes" if (args.grep and args.grep_case_sensitive) else None,
|
|
543
|
+
}
|
|
544
|
+
print(render_picker(
|
|
545
|
+
rows=rows,
|
|
546
|
+
agent_by_sid=agent_by_sid,
|
|
547
|
+
org=args.org,
|
|
548
|
+
tr=tr,
|
|
549
|
+
filters=filters,
|
|
550
|
+
composed_sql=sql,
|
|
551
|
+
))
|
|
552
|
+
return 0 if rows else 2
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
if __name__ == "__main__":
|
|
556
|
+
raise SystemExit(main())
|