@salesforce/afv-skills 1.13.0 → 1.15.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 +3 -3
- 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-ui-bundle-app/SKILL.md +33 -8
- package/skills/generating-custom-application/SKILL.md +1 -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-ui-bundle-custom-app/SKILL.md +93 -0
- package/skills/generating-ui-bundle-custom-app/docs/configure-metadata-custom-application.md +70 -0
- package/skills/generating-ui-bundle-metadata/SKILL.md +39 -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/reviewing-lwc-mobile-offline/SKILL.md +168 -0
- package/skills/reviewing-lwc-mobile-offline/references/grounding.md +7 -0
- package/skills/reviewing-lwc-mobile-offline/references/inline-graphql.md +43 -0
- package/skills/reviewing-lwc-mobile-offline/references/komaci-eslint.md +125 -0
- package/skills/reviewing-lwc-mobile-offline/references/lwc-if.md +78 -0
- package/skills/reviewing-lwc-mobile-offline/scripts/komaci.config.mjs +18 -0
- package/skills/reviewing-lwc-mobile-offline/scripts/package.json +10 -0
- package/skills/reviewing-lwc-mobile-offline/scripts/run-komaci.sh +69 -0
- 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/using-mobile-native-capabilities/SKILL.md +182 -0
- package/skills/using-mobile-native-capabilities/references/app-review.md +68 -0
- package/skills/using-mobile-native-capabilities/references/ar-space-capture.md +125 -0
- package/skills/using-mobile-native-capabilities/references/barcode-scanner.md +219 -0
- package/skills/using-mobile-native-capabilities/references/base-capability.md +22 -0
- package/skills/using-mobile-native-capabilities/references/biometrics.md +90 -0
- package/skills/using-mobile-native-capabilities/references/calendar.md +213 -0
- package/skills/using-mobile-native-capabilities/references/contacts.md +232 -0
- package/skills/using-mobile-native-capabilities/references/document-scanner.md +342 -0
- package/skills/using-mobile-native-capabilities/references/geofencing.md +123 -0
- package/skills/using-mobile-native-capabilities/references/location.md +158 -0
- package/skills/using-mobile-native-capabilities/references/mobile-capabilities.md +30 -0
- package/skills/using-mobile-native-capabilities/references/nfc.md +181 -0
- package/skills/using-mobile-native-capabilities/references/payments.md +95 -0
- 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
package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Tests for ``parse_wave`` classifiers + BFS step helper.
|
|
2
|
+
|
|
3
|
+
Targets the high-density branch surface in ``classify_bundle_action``
|
|
4
|
+
(Flow / Apex / PromptTemplate / StandardAction / Unknown) and
|
|
5
|
+
``classify_action_call`` (apex / generatePromptResponse / other / empty).
|
|
6
|
+
Plus the central ``bfs_step`` helper that drives wave-level fetches.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import unittest
|
|
11
|
+
|
|
12
|
+
from . import _bootstrap # noqa: F401 — sys.path setup
|
|
13
|
+
|
|
14
|
+
import parse_wave # type: ignore
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# -----------------------------------------------------------------------------
|
|
18
|
+
# classify_bundle_action — every branch
|
|
19
|
+
# -----------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ClassifyBundleActionTests(unittest.TestCase):
|
|
23
|
+
|
|
24
|
+
def test_no_target_returns_none_pair(self):
|
|
25
|
+
unwraps, leaf = parse_wave.classify_bundle_action({
|
|
26
|
+
"invocationTarget": "",
|
|
27
|
+
"invocationTargetType": "flow",
|
|
28
|
+
})
|
|
29
|
+
self.assertIsNone(unwraps)
|
|
30
|
+
self.assertIsNone(leaf)
|
|
31
|
+
|
|
32
|
+
def test_flow_branch(self):
|
|
33
|
+
unwraps, leaf = parse_wave.classify_bundle_action({
|
|
34
|
+
"invocationTarget": "MyFlow",
|
|
35
|
+
"invocationTargetType": "flow",
|
|
36
|
+
})
|
|
37
|
+
self.assertEqual(unwraps["kind"], "FLOW")
|
|
38
|
+
self.assertEqual(unwraps["api_name"], "MyFlow")
|
|
39
|
+
self.assertEqual(leaf["children"], [])
|
|
40
|
+
|
|
41
|
+
def test_apex_branch(self):
|
|
42
|
+
unwraps, leaf = parse_wave.classify_bundle_action({
|
|
43
|
+
"invocationTarget": "MyClass.method",
|
|
44
|
+
"invocationTargetType": "apex",
|
|
45
|
+
})
|
|
46
|
+
self.assertEqual(unwraps["kind"], "APEX")
|
|
47
|
+
self.assertEqual(leaf["api_name"], "MyClass.method")
|
|
48
|
+
|
|
49
|
+
def test_prompt_template_via_generatepromptresponse(self):
|
|
50
|
+
unwraps, _ = parse_wave.classify_bundle_action({
|
|
51
|
+
"invocationTarget": "MyPrompt",
|
|
52
|
+
"invocationTargetType": "generatepromptresponse",
|
|
53
|
+
})
|
|
54
|
+
self.assertEqual(unwraps["kind"], "PROMPT_TEMPLATE")
|
|
55
|
+
|
|
56
|
+
def test_prompt_template_via_prompt_prefix(self):
|
|
57
|
+
unwraps, _ = parse_wave.classify_bundle_action({
|
|
58
|
+
"invocationTarget": "MyPrompt",
|
|
59
|
+
"invocationTargetType": "prompt-template",
|
|
60
|
+
})
|
|
61
|
+
self.assertEqual(unwraps["kind"], "PROMPT_TEMPLATE")
|
|
62
|
+
|
|
63
|
+
def test_prompt_template_via_genai_prefix(self):
|
|
64
|
+
unwraps, _ = parse_wave.classify_bundle_action({
|
|
65
|
+
"invocationTarget": "MyPrompt",
|
|
66
|
+
"invocationTargetType": "genaifunction",
|
|
67
|
+
})
|
|
68
|
+
self.assertEqual(unwraps["kind"], "PROMPT_TEMPLATE")
|
|
69
|
+
|
|
70
|
+
def test_standard_invocable_action_branch(self):
|
|
71
|
+
unwraps, leaf = parse_wave.classify_bundle_action({
|
|
72
|
+
"invocationTarget": "createRecord",
|
|
73
|
+
"invocationTargetType": "standardinvocableaction",
|
|
74
|
+
})
|
|
75
|
+
self.assertEqual(unwraps["kind"], "STANDARD_ACTION")
|
|
76
|
+
self.assertEqual(unwraps["invocation_type"], "standardinvocableaction")
|
|
77
|
+
self.assertEqual(leaf["api_name"], "createRecord")
|
|
78
|
+
|
|
79
|
+
def test_unknown_action_type_falls_to_unknown(self):
|
|
80
|
+
unwraps, leaf = parse_wave.classify_bundle_action({
|
|
81
|
+
"invocationTarget": "MysteryAction",
|
|
82
|
+
"invocationTargetType": "MYSTERY",
|
|
83
|
+
})
|
|
84
|
+
self.assertEqual(unwraps["kind"], "UNKNOWN")
|
|
85
|
+
self.assertEqual(unwraps["invocation_type"], "mystery") # lowercased
|
|
86
|
+
self.assertEqual(leaf["kind"], "UNKNOWN")
|
|
87
|
+
|
|
88
|
+
def test_invocationTargetType_missing_normalizes_to_empty_string(self):
|
|
89
|
+
unwraps, _ = parse_wave.classify_bundle_action({
|
|
90
|
+
"invocationTarget": "MyAction",
|
|
91
|
+
# no invocationTargetType — coerced to ""
|
|
92
|
+
})
|
|
93
|
+
self.assertEqual(unwraps["kind"], "UNKNOWN")
|
|
94
|
+
self.assertEqual(unwraps["invocation_type"], "")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# -----------------------------------------------------------------------------
|
|
98
|
+
# classify_action_call — every branch
|
|
99
|
+
# -----------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ClassifyActionCallTests(unittest.TestCase):
|
|
103
|
+
|
|
104
|
+
def test_apex_branch(self):
|
|
105
|
+
out = parse_wave.classify_action_call("apex", "MyClass", "callA")
|
|
106
|
+
self.assertEqual(out["kind"], "APEX")
|
|
107
|
+
self.assertEqual(out["api_name"], "MyClass")
|
|
108
|
+
self.assertEqual(out["element_name"], "callA")
|
|
109
|
+
|
|
110
|
+
def test_generate_prompt_response_branch(self):
|
|
111
|
+
out = parse_wave.classify_action_call(
|
|
112
|
+
"generatePromptResponse", "MyPrompt", "callB",
|
|
113
|
+
)
|
|
114
|
+
self.assertEqual(out["kind"], "PROMPT_TEMPLATE")
|
|
115
|
+
self.assertEqual(out["api_name"], "MyPrompt")
|
|
116
|
+
|
|
117
|
+
def test_other_action_type_falls_to_standard_action(self):
|
|
118
|
+
out = parse_wave.classify_action_call("emailAlert", "MyAlert", "callC")
|
|
119
|
+
self.assertEqual(out["kind"], "STANDARD_ACTION")
|
|
120
|
+
self.assertEqual(out["invocation_type"], "emailAlert")
|
|
121
|
+
self.assertEqual(out["api_name"], "MyAlert")
|
|
122
|
+
|
|
123
|
+
def test_other_action_type_uses_action_type_when_name_missing(self):
|
|
124
|
+
out = parse_wave.classify_action_call("emailAlert", "", "callC")
|
|
125
|
+
self.assertEqual(out["api_name"], "emailAlert")
|
|
126
|
+
|
|
127
|
+
def test_empty_action_type_falls_to_unknown(self):
|
|
128
|
+
out = parse_wave.classify_action_call("", "MyName", "callD")
|
|
129
|
+
self.assertEqual(out["kind"], "UNKNOWN")
|
|
130
|
+
self.assertEqual(out["api_name"], "MyName")
|
|
131
|
+
|
|
132
|
+
def test_unknown_with_no_name_falls_to_question_mark(self):
|
|
133
|
+
out = parse_wave.classify_action_call("", "", "callE")
|
|
134
|
+
self.assertEqual(out["api_name"], "?")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# -----------------------------------------------------------------------------
|
|
138
|
+
# bfs_step — pure helper
|
|
139
|
+
# -----------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class BfsStepTests(unittest.TestCase):
|
|
143
|
+
|
|
144
|
+
def test_new_refs_added_to_pending(self):
|
|
145
|
+
pending = parse_wave.empty_kind_sets()
|
|
146
|
+
visited = parse_wave.empty_kind_sets()
|
|
147
|
+
new_refs = {
|
|
148
|
+
"FLOW": {"FlowA", "FlowB"},
|
|
149
|
+
"APEX": {"ApexA"},
|
|
150
|
+
"PROMPT_TEMPLATE": set(),
|
|
151
|
+
"STANDARD_ACTION": set(),
|
|
152
|
+
}
|
|
153
|
+
merged, cycles = parse_wave.bfs_step(pending, visited, new_refs)
|
|
154
|
+
self.assertEqual(merged["FLOW"], {"FlowA", "FlowB"})
|
|
155
|
+
self.assertEqual(merged["APEX"], {"ApexA"})
|
|
156
|
+
self.assertEqual(cycles, [])
|
|
157
|
+
|
|
158
|
+
def test_visited_refs_recorded_as_cycles(self):
|
|
159
|
+
pending = parse_wave.empty_kind_sets()
|
|
160
|
+
visited = parse_wave.empty_kind_sets()
|
|
161
|
+
visited["FLOW"].add("FlowA")
|
|
162
|
+
new_refs = {
|
|
163
|
+
"FLOW": {"FlowA", "FlowB"},
|
|
164
|
+
"APEX": set(), "PROMPT_TEMPLATE": set(), "STANDARD_ACTION": set(),
|
|
165
|
+
}
|
|
166
|
+
merged, cycles = parse_wave.bfs_step(pending, visited, new_refs)
|
|
167
|
+
self.assertEqual(merged["FLOW"], {"FlowB"})
|
|
168
|
+
self.assertIn(("FLOW", "FlowA"), cycles)
|
|
169
|
+
|
|
170
|
+
def test_cross_kind_same_name_stays_distinct(self):
|
|
171
|
+
pending = parse_wave.empty_kind_sets()
|
|
172
|
+
visited = parse_wave.empty_kind_sets()
|
|
173
|
+
new_refs = {
|
|
174
|
+
"FLOW": {"Foo"}, "APEX": {"Foo"},
|
|
175
|
+
"PROMPT_TEMPLATE": set(), "STANDARD_ACTION": set(),
|
|
176
|
+
}
|
|
177
|
+
merged, _ = parse_wave.bfs_step(pending, visited, new_refs)
|
|
178
|
+
self.assertEqual(merged["FLOW"], {"Foo"})
|
|
179
|
+
self.assertEqual(merged["APEX"], {"Foo"})
|
|
180
|
+
|
|
181
|
+
def test_existing_pending_preserved_via_merge(self):
|
|
182
|
+
pending = parse_wave.empty_kind_sets()
|
|
183
|
+
pending["FLOW"].add("FlowExisting")
|
|
184
|
+
visited = parse_wave.empty_kind_sets()
|
|
185
|
+
new_refs = {
|
|
186
|
+
"FLOW": {"FlowNew"},
|
|
187
|
+
"APEX": set(), "PROMPT_TEMPLATE": set(), "STANDARD_ACTION": set(),
|
|
188
|
+
}
|
|
189
|
+
merged, _ = parse_wave.bfs_step(pending, visited, new_refs)
|
|
190
|
+
self.assertEqual(merged["FLOW"], {"FlowExisting", "FlowNew"})
|
|
191
|
+
|
|
192
|
+
def test_unknown_kind_raises(self):
|
|
193
|
+
pending = parse_wave.empty_kind_sets()
|
|
194
|
+
visited = parse_wave.empty_kind_sets()
|
|
195
|
+
with self.assertRaises(ValueError) as ctx:
|
|
196
|
+
parse_wave.bfs_step(
|
|
197
|
+
pending, visited,
|
|
198
|
+
{"BOGUS_KIND": {"x"}},
|
|
199
|
+
)
|
|
200
|
+
self.assertIn("unknown BFS kind", str(ctx.exception))
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# -----------------------------------------------------------------------------
|
|
204
|
+
# empty_kind_sets — fresh dict per call
|
|
205
|
+
# -----------------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class EmptyKindSetsTests(unittest.TestCase):
|
|
209
|
+
|
|
210
|
+
def test_returns_one_set_per_BFS_KIND(self):
|
|
211
|
+
out = parse_wave.empty_kind_sets()
|
|
212
|
+
self.assertEqual(set(out.keys()), set(parse_wave.BFS_KINDS))
|
|
213
|
+
for v in out.values():
|
|
214
|
+
self.assertEqual(v, set())
|
|
215
|
+
|
|
216
|
+
def test_each_call_returns_fresh_dict(self):
|
|
217
|
+
a = parse_wave.empty_kind_sets()
|
|
218
|
+
a["FLOW"].add("x")
|
|
219
|
+
b = parse_wave.empty_kind_sets()
|
|
220
|
+
self.assertEqual(b["FLOW"], set())
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
if __name__ == "__main__":
|
|
224
|
+
unittest.main()
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"""Tests for ``parse_wave`` standalone helpers — non-classifier surface.
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- ``compute_stats`` node count + depth + per-kind counter
|
|
5
|
+
- ``finalize_cap`` drains pending → unresolved + sets partial
|
|
6
|
+
- ``atomic_write_json`` tmp + os.replace
|
|
7
|
+
- ``init_tree`` bundle + bot_def shape
|
|
8
|
+
- ``harvest_waves`` Flow XML walking with subflows + actionCalls
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import unittest
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from tempfile import TemporaryDirectory
|
|
17
|
+
|
|
18
|
+
from . import _bootstrap # noqa: F401 — sys.path setup
|
|
19
|
+
|
|
20
|
+
import parse_wave # type: ignore
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_FLOW_NS = "http://soap.sforce.com/2006/04/metadata"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# -----------------------------------------------------------------------------
|
|
27
|
+
# compute_stats
|
|
28
|
+
# -----------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ComputeStatsTests(unittest.TestCase):
|
|
32
|
+
|
|
33
|
+
def test_empty_root_returns_zero_counts(self):
|
|
34
|
+
n, d, counts = parse_wave.compute_stats({})
|
|
35
|
+
self.assertEqual(n, 0)
|
|
36
|
+
self.assertEqual(d, 0)
|
|
37
|
+
self.assertEqual(counts, {})
|
|
38
|
+
|
|
39
|
+
def test_single_node_counts_kind(self):
|
|
40
|
+
n, d, counts = parse_wave.compute_stats({"kind": "BOT_DEFINITION"})
|
|
41
|
+
self.assertEqual(n, 1)
|
|
42
|
+
self.assertEqual(counts["BOT_DEFINITION"], 1)
|
|
43
|
+
self.assertEqual(d, 0)
|
|
44
|
+
|
|
45
|
+
def test_nested_tree_returns_max_depth(self):
|
|
46
|
+
root = {
|
|
47
|
+
"kind": "BOT_DEFINITION",
|
|
48
|
+
"children": [
|
|
49
|
+
{"kind": "TOPIC", "children": [
|
|
50
|
+
{"kind": "FLOW", "children": [
|
|
51
|
+
{"kind": "APEX"},
|
|
52
|
+
]},
|
|
53
|
+
]},
|
|
54
|
+
{"kind": "STANDARD_ACTION"},
|
|
55
|
+
],
|
|
56
|
+
}
|
|
57
|
+
n, d, counts = parse_wave.compute_stats(root)
|
|
58
|
+
self.assertEqual(n, 5)
|
|
59
|
+
self.assertEqual(d, 3)
|
|
60
|
+
self.assertEqual(counts["BOT_DEFINITION"], 1)
|
|
61
|
+
self.assertEqual(counts["TOPIC"], 1)
|
|
62
|
+
self.assertEqual(counts["FLOW"], 1)
|
|
63
|
+
self.assertEqual(counts["APEX"], 1)
|
|
64
|
+
self.assertEqual(counts["STANDARD_ACTION"], 1)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# -----------------------------------------------------------------------------
|
|
68
|
+
# finalize_cap
|
|
69
|
+
# -----------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class FinalizeCapTests(unittest.TestCase):
|
|
73
|
+
|
|
74
|
+
def test_drains_pending_into_unresolved(self):
|
|
75
|
+
tree = {
|
|
76
|
+
"_pending_fetches": {
|
|
77
|
+
"FLOW": ["FlowA", "FlowB"],
|
|
78
|
+
"APEX": ["ClassA"],
|
|
79
|
+
"PROMPT_TEMPLATE": [], "STANDARD_ACTION": [],
|
|
80
|
+
},
|
|
81
|
+
"_unresolved": [],
|
|
82
|
+
}
|
|
83
|
+
out = parse_wave.finalize_cap(tree)
|
|
84
|
+
self.assertEqual(len(out["_unresolved"]), 3)
|
|
85
|
+
# Pending wiped per-kind
|
|
86
|
+
for kind in parse_wave.BFS_KINDS:
|
|
87
|
+
self.assertEqual(out["_pending_fetches"][kind], [])
|
|
88
|
+
# _partial flipped
|
|
89
|
+
self.assertTrue(out["_partial"])
|
|
90
|
+
self.assertEqual(out["_partial_reason"], "max-wave-depth")
|
|
91
|
+
|
|
92
|
+
def test_does_not_overwrite_existing_partial_reason(self):
|
|
93
|
+
tree = {
|
|
94
|
+
"_pending_fetches": {"FLOW": ["FlowA"], "APEX": [],
|
|
95
|
+
"PROMPT_TEMPLATE": [], "STANDARD_ACTION": []},
|
|
96
|
+
"_unresolved": [],
|
|
97
|
+
"_partial_reason": "max-depth-cap", # earlier-set, more specific
|
|
98
|
+
}
|
|
99
|
+
out = parse_wave.finalize_cap(tree)
|
|
100
|
+
# Reason kept; depth-cap wins priority over wave-depth.
|
|
101
|
+
self.assertEqual(out["_partial_reason"], "max-depth-cap")
|
|
102
|
+
|
|
103
|
+
def test_no_pending_no_changes(self):
|
|
104
|
+
tree = {
|
|
105
|
+
"_pending_fetches": {k: [] for k in parse_wave.BFS_KINDS},
|
|
106
|
+
"_unresolved": [],
|
|
107
|
+
"_partial": False,
|
|
108
|
+
"_partial_reason": None,
|
|
109
|
+
}
|
|
110
|
+
out = parse_wave.finalize_cap(tree)
|
|
111
|
+
self.assertEqual(out["_unresolved"], [])
|
|
112
|
+
# Nothing drained, _partial untouched
|
|
113
|
+
self.assertFalse(out["_partial"])
|
|
114
|
+
|
|
115
|
+
def test_unresolved_entry_shape(self):
|
|
116
|
+
tree = {
|
|
117
|
+
"_pending_fetches": {"FLOW": ["FlowA"], "APEX": [],
|
|
118
|
+
"PROMPT_TEMPLATE": [], "STANDARD_ACTION": []},
|
|
119
|
+
"_unresolved": [],
|
|
120
|
+
}
|
|
121
|
+
out = parse_wave.finalize_cap(tree)
|
|
122
|
+
entry = out["_unresolved"][0]
|
|
123
|
+
self.assertEqual(entry["kind"], "FLOW")
|
|
124
|
+
self.assertEqual(entry["api_name"], "FlowA")
|
|
125
|
+
self.assertEqual(entry["reason"], "max-wave-depth exceeded")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# -----------------------------------------------------------------------------
|
|
129
|
+
# atomic_write_json
|
|
130
|
+
# -----------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class AtomicWriteJsonTests(unittest.TestCase):
|
|
134
|
+
|
|
135
|
+
def test_writes_indented_json(self):
|
|
136
|
+
with TemporaryDirectory() as t:
|
|
137
|
+
target = Path(t) / "out.json"
|
|
138
|
+
parse_wave.atomic_write_json(target, {"a": 1, "b": [1, 2]})
|
|
139
|
+
data = json.loads(target.read_text())
|
|
140
|
+
self.assertEqual(data, {"a": 1, "b": [1, 2]})
|
|
141
|
+
|
|
142
|
+
def test_overwrites_existing(self):
|
|
143
|
+
with TemporaryDirectory() as t:
|
|
144
|
+
target = Path(t) / "out.json"
|
|
145
|
+
target.write_text("old")
|
|
146
|
+
parse_wave.atomic_write_json(target, {"new": True})
|
|
147
|
+
data = json.loads(target.read_text())
|
|
148
|
+
self.assertEqual(data, {"new": True})
|
|
149
|
+
|
|
150
|
+
def test_no_tmp_left_behind(self):
|
|
151
|
+
with TemporaryDirectory() as t:
|
|
152
|
+
target = Path(t) / "out.json"
|
|
153
|
+
parse_wave.atomic_write_json(target, {"x": 1})
|
|
154
|
+
tmp_sibling = target.with_suffix(target.suffix + ".tmp")
|
|
155
|
+
self.assertFalse(tmp_sibling.exists())
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# -----------------------------------------------------------------------------
|
|
159
|
+
# init_tree
|
|
160
|
+
# -----------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class InitTreeTests(unittest.TestCase):
|
|
164
|
+
|
|
165
|
+
def _setup_env(self, **overrides) -> dict:
|
|
166
|
+
defaults = {
|
|
167
|
+
"AGENT_API_NAME": "DemoAgent",
|
|
168
|
+
"AGENT_VERSION": "v3",
|
|
169
|
+
"BOT_ID": "0Xx000000000ABC",
|
|
170
|
+
"BOT_MASTER_LABEL": "Demo Agent",
|
|
171
|
+
"VERSION_AUTO_PICKED": "false",
|
|
172
|
+
}
|
|
173
|
+
defaults.update(overrides)
|
|
174
|
+
return defaults
|
|
175
|
+
|
|
176
|
+
def _run(self, *, work_dir: Path, env: dict, bundle: dict) -> dict:
|
|
177
|
+
old = dict(os.environ)
|
|
178
|
+
try:
|
|
179
|
+
os.environ.update(env)
|
|
180
|
+
return parse_wave.init_tree(work_dir, bundle)
|
|
181
|
+
finally:
|
|
182
|
+
os.environ.clear()
|
|
183
|
+
os.environ.update(old)
|
|
184
|
+
|
|
185
|
+
def test_initial_tree_shape(self):
|
|
186
|
+
with TemporaryDirectory() as t:
|
|
187
|
+
tree = self._run(
|
|
188
|
+
work_dir=Path(t),
|
|
189
|
+
env=self._setup_env(),
|
|
190
|
+
bundle={"generation": "nga", "plannerName": "MyPlanner",
|
|
191
|
+
"plannerType": "Atlas__Reasoning"},
|
|
192
|
+
)
|
|
193
|
+
self.assertEqual(tree["_schema_version"], "3.1")
|
|
194
|
+
self.assertEqual(tree["agent"]["api_name"], "DemoAgent")
|
|
195
|
+
self.assertEqual(tree["agent"]["version"], "v3")
|
|
196
|
+
self.assertEqual(tree["agent"]["generation"], "nga")
|
|
197
|
+
self.assertEqual(tree["agent"]["planner_name"], "MyPlanner")
|
|
198
|
+
self.assertEqual(tree["root"]["kind"], "BOT_DEFINITION")
|
|
199
|
+
self.assertEqual(tree["root"]["children"], [])
|
|
200
|
+
self.assertTrue(tree["_partial"])
|
|
201
|
+
self.assertIsNone(tree["_partial_reason"])
|
|
202
|
+
# _pending_fetches has all 4 BFS kinds
|
|
203
|
+
for kind in parse_wave.BFS_KINDS:
|
|
204
|
+
self.assertIn(kind, tree["_pending_fetches"])
|
|
205
|
+
|
|
206
|
+
def test_pulls_bot_definition_metadata_from_disk(self):
|
|
207
|
+
with TemporaryDirectory() as t:
|
|
208
|
+
work_dir = Path(t)
|
|
209
|
+
(work_dir / "_bot_definition.json").write_text(json.dumps({
|
|
210
|
+
"result": {"records": [{
|
|
211
|
+
"DeveloperName": "DemoAgent",
|
|
212
|
+
"MasterLabel": "Demo (from disk)",
|
|
213
|
+
"Description": "via _bot_definition.json",
|
|
214
|
+
"AgentType": "Internal",
|
|
215
|
+
"Type": "AiCopilot",
|
|
216
|
+
"AgentTemplate": "TPL",
|
|
217
|
+
"BotSource": "AgentforceAgentCopilot",
|
|
218
|
+
}]},
|
|
219
|
+
}))
|
|
220
|
+
tree = self._run(
|
|
221
|
+
work_dir=work_dir,
|
|
222
|
+
env=self._setup_env(),
|
|
223
|
+
bundle={"generation": "classic"},
|
|
224
|
+
)
|
|
225
|
+
# MasterLabel from _bot_definition.json wins over env
|
|
226
|
+
self.assertEqual(tree["agent"]["master_label"], "Demo (from disk)")
|
|
227
|
+
self.assertEqual(tree["agent"]["description"], "via _bot_definition.json")
|
|
228
|
+
self.assertEqual(tree["agent"]["agent_type"], "Internal")
|
|
229
|
+
|
|
230
|
+
def test_version_auto_picked_flag_propagates(self):
|
|
231
|
+
with TemporaryDirectory() as t:
|
|
232
|
+
tree = self._run(
|
|
233
|
+
work_dir=Path(t),
|
|
234
|
+
env=self._setup_env(VERSION_AUTO_PICKED="true"),
|
|
235
|
+
bundle={"generation": "nga"},
|
|
236
|
+
)
|
|
237
|
+
self.assertTrue(tree["agent"]["_version_auto_picked"])
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# -----------------------------------------------------------------------------
|
|
241
|
+
# harvest_waves
|
|
242
|
+
# -----------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _flow_xml(*, name: str, action_calls: list[dict] | None = None,
|
|
246
|
+
subflows: list[str] | None = None) -> str:
|
|
247
|
+
"""Build a minimal Flow XML body."""
|
|
248
|
+
acs = "".join(
|
|
249
|
+
f""" <actionCalls>
|
|
250
|
+
<name>{ac['name']}</name>
|
|
251
|
+
<actionType>{ac['actionType']}</actionType>
|
|
252
|
+
<actionName>{ac['actionName']}</actionName>
|
|
253
|
+
</actionCalls>
|
|
254
|
+
"""
|
|
255
|
+
for ac in (action_calls or [])
|
|
256
|
+
)
|
|
257
|
+
subs = "".join(
|
|
258
|
+
f""" <subflows>
|
|
259
|
+
<name>sub_{i}</name>
|
|
260
|
+
<flowName>{fn}</flowName>
|
|
261
|
+
</subflows>
|
|
262
|
+
"""
|
|
263
|
+
for i, fn in enumerate(subflows or [])
|
|
264
|
+
)
|
|
265
|
+
return f"""<?xml version="1.0" encoding="UTF-8"?>
|
|
266
|
+
<Flow xmlns="{_FLOW_NS}">
|
|
267
|
+
<fullName>{name}</fullName>
|
|
268
|
+
{acs}{subs}</Flow>
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class HarvestWavesTests(unittest.TestCase):
|
|
273
|
+
|
|
274
|
+
def _setup_wave(self, work_dir: Path, *, flow_files: dict[str, str],
|
|
275
|
+
apex_class_names: list[str] | None = None,
|
|
276
|
+
prompt_template_names: list[str] | None = None) -> None:
|
|
277
|
+
"""Plant a wave1/unpackaged/{flows,classes,genAiPromptTemplates}/
|
|
278
|
+
directory tree under work_dir/sf_meta/."""
|
|
279
|
+
wave = work_dir / "sf_meta" / "wave1" / "unpackaged"
|
|
280
|
+
flows_dir = wave / "flows"
|
|
281
|
+
flows_dir.mkdir(parents=True)
|
|
282
|
+
for filename, body in flow_files.items():
|
|
283
|
+
(flows_dir / filename).write_text(body)
|
|
284
|
+
if apex_class_names:
|
|
285
|
+
classes_dir = wave / "classes"
|
|
286
|
+
classes_dir.mkdir()
|
|
287
|
+
for n in apex_class_names:
|
|
288
|
+
(classes_dir / f"{n}.cls-meta.xml").write_text("<x/>")
|
|
289
|
+
if prompt_template_names:
|
|
290
|
+
prompt_dir = wave / "genAiPromptTemplates"
|
|
291
|
+
prompt_dir.mkdir()
|
|
292
|
+
for n in prompt_template_names:
|
|
293
|
+
(prompt_dir / f"{n}.genAiPromptTemplate").write_text("<x/>")
|
|
294
|
+
|
|
295
|
+
def test_returns_empty_when_no_sf_meta(self):
|
|
296
|
+
with TemporaryDirectory() as t:
|
|
297
|
+
flow_children, vis, pend, cycles = parse_wave.harvest_waves(Path(t))
|
|
298
|
+
self.assertEqual(flow_children, {})
|
|
299
|
+
self.assertEqual(cycles, [])
|
|
300
|
+
for v in vis.values():
|
|
301
|
+
self.assertEqual(v, set())
|
|
302
|
+
|
|
303
|
+
def test_harvests_flow_action_calls_and_subflows(self):
|
|
304
|
+
with TemporaryDirectory() as t:
|
|
305
|
+
work_dir = Path(t)
|
|
306
|
+
self._setup_wave(work_dir, flow_files={
|
|
307
|
+
"MainFlow.flow": _flow_xml(
|
|
308
|
+
name="MainFlow",
|
|
309
|
+
action_calls=[
|
|
310
|
+
{"name": "callA", "actionType": "apex",
|
|
311
|
+
"actionName": "MyClass"},
|
|
312
|
+
{"name": "callB", "actionType": "generatePromptResponse",
|
|
313
|
+
"actionName": "MyPrompt"},
|
|
314
|
+
],
|
|
315
|
+
subflows=["NestedFlow"],
|
|
316
|
+
),
|
|
317
|
+
})
|
|
318
|
+
flow_children, visited, pending, _cycles = parse_wave.harvest_waves(work_dir)
|
|
319
|
+
# MainFlow visited
|
|
320
|
+
self.assertIn("MainFlow", visited["FLOW"])
|
|
321
|
+
# Apex + prompt-template + nested flow ref harvested into pending
|
|
322
|
+
self.assertIn("MyClass", pending["APEX"])
|
|
323
|
+
self.assertIn("MyPrompt", pending["PROMPT_TEMPLATE"])
|
|
324
|
+
self.assertIn("NestedFlow", pending["FLOW"])
|
|
325
|
+
# MainFlow's children include the apex + prompt-template + subflow
|
|
326
|
+
self.assertEqual(len(flow_children["MainFlow"]), 3)
|
|
327
|
+
|
|
328
|
+
def test_apex_classes_marked_visited(self):
|
|
329
|
+
with TemporaryDirectory() as t:
|
|
330
|
+
work_dir = Path(t)
|
|
331
|
+
self._setup_wave(
|
|
332
|
+
work_dir,
|
|
333
|
+
flow_files={"F.flow": _flow_xml(name="F")},
|
|
334
|
+
apex_class_names=["AlreadyApex"],
|
|
335
|
+
)
|
|
336
|
+
_, visited, _, _ = parse_wave.harvest_waves(work_dir)
|
|
337
|
+
self.assertIn("AlreadyApex", visited["APEX"])
|
|
338
|
+
|
|
339
|
+
def test_prompt_templates_marked_visited(self):
|
|
340
|
+
with TemporaryDirectory() as t:
|
|
341
|
+
work_dir = Path(t)
|
|
342
|
+
self._setup_wave(
|
|
343
|
+
work_dir,
|
|
344
|
+
flow_files={"F.flow": _flow_xml(name="F")},
|
|
345
|
+
prompt_template_names=["AlreadyPrompt"],
|
|
346
|
+
)
|
|
347
|
+
_, visited, _, _ = parse_wave.harvest_waves(work_dir)
|
|
348
|
+
self.assertIn("AlreadyPrompt", visited["PROMPT_TEMPLATE"])
|
|
349
|
+
|
|
350
|
+
def test_already_visited_apex_pruned_from_pending(self):
|
|
351
|
+
"""When pass-2/3 marks an Apex class visited AFTER pass-1 routed it
|
|
352
|
+
into pending, the post-pass prune drops it from pending."""
|
|
353
|
+
with TemporaryDirectory() as t:
|
|
354
|
+
work_dir = Path(t)
|
|
355
|
+
self._setup_wave(work_dir,
|
|
356
|
+
flow_files={"F.flow": _flow_xml(
|
|
357
|
+
name="F",
|
|
358
|
+
action_calls=[{
|
|
359
|
+
"name": "callA", "actionType": "apex",
|
|
360
|
+
"actionName": "ClassX",
|
|
361
|
+
}],
|
|
362
|
+
)},
|
|
363
|
+
apex_class_names=["ClassX"])
|
|
364
|
+
_, visited, pending, _ = parse_wave.harvest_waves(work_dir)
|
|
365
|
+
self.assertIn("ClassX", visited["APEX"])
|
|
366
|
+
# Pruned because already-visited (Apex class file present this wave)
|
|
367
|
+
self.assertNotIn("ClassX", pending["APEX"])
|
|
368
|
+
|
|
369
|
+
def test_malformed_flow_xml_returns_empty_children(self):
|
|
370
|
+
with TemporaryDirectory() as t:
|
|
371
|
+
work_dir = Path(t)
|
|
372
|
+
self._setup_wave(work_dir, flow_files={
|
|
373
|
+
"Broken.flow": "<<<not xml>>>",
|
|
374
|
+
})
|
|
375
|
+
flow_children, _, _, _ = parse_wave.harvest_waves(work_dir)
|
|
376
|
+
self.assertEqual(flow_children["Broken"], [])
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
if __name__ == "__main__":
|
|
380
|
+
unittest.main()
|