@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.
Files changed (365) hide show
  1. package/package.json +1 -1
  2. package/skills/activating-datacloud/SKILL.md +0 -1
  3. package/skills/analyzing-omnistudio-dependencies/SKILL.md +0 -1
  4. package/skills/applying-slds/SKILL.md +322 -0
  5. package/skills/applying-slds/checklists.md +83 -0
  6. package/skills/applying-slds/examples.md +283 -0
  7. package/skills/applying-slds/guidance/README.md +83 -0
  8. package/skills/applying-slds/guidance/blueprints-index.md +213 -0
  9. package/skills/applying-slds/guidance/icons-guidance.md +186 -0
  10. package/skills/applying-slds/guidance/overviews/borders.md +236 -0
  11. package/skills/applying-slds/guidance/overviews/color.md +266 -0
  12. package/skills/applying-slds/guidance/overviews/display-density.md +366 -0
  13. package/skills/applying-slds/guidance/overviews/icons.md +240 -0
  14. package/skills/applying-slds/guidance/overviews/illustrations.md +235 -0
  15. package/skills/applying-slds/guidance/overviews/shadows.md +176 -0
  16. package/skills/applying-slds/guidance/overviews/spacing.md +216 -0
  17. package/skills/applying-slds/guidance/overviews/typography.md +323 -0
  18. package/skills/applying-slds/guidance/overviews/utilities.md +542 -0
  19. package/skills/applying-slds/guidance/slds-development-guide.md +288 -0
  20. package/skills/applying-slds/guidance/styling-hooks/borders.md +202 -0
  21. package/skills/applying-slds/guidance/styling-hooks/color/expressive-palette-hooks.md +153 -0
  22. package/skills/applying-slds/guidance/styling-hooks/color/index.md +171 -0
  23. package/skills/applying-slds/guidance/styling-hooks/color/semantic/accent-hooks.md +204 -0
  24. package/skills/applying-slds/guidance/styling-hooks/color/semantic/feedback-hooks.md +768 -0
  25. package/skills/applying-slds/guidance/styling-hooks/color/semantic/surface-hooks.md +337 -0
  26. package/skills/applying-slds/guidance/styling-hooks/color/system-hooks.md +132 -0
  27. package/skills/applying-slds/guidance/styling-hooks/index.md +327 -0
  28. package/skills/applying-slds/guidance/styling-hooks/shadows.md +238 -0
  29. package/skills/applying-slds/guidance/styling-hooks/spacing.md +254 -0
  30. package/skills/applying-slds/guidance/styling-hooks/typography.md +448 -0
  31. package/skills/applying-slds/guidance/utilities/alignment.md +119 -0
  32. package/skills/applying-slds/guidance/utilities/borders.md +131 -0
  33. package/skills/applying-slds/guidance/utilities/box.md +125 -0
  34. package/skills/applying-slds/guidance/utilities/color.md +165 -0
  35. package/skills/applying-slds/guidance/utilities/dark-mode.md +111 -0
  36. package/skills/applying-slds/guidance/utilities/description-list.md +168 -0
  37. package/skills/applying-slds/guidance/utilities/floats.md +117 -0
  38. package/skills/applying-slds/guidance/utilities/grid.md +264 -0
  39. package/skills/applying-slds/guidance/utilities/horizontal-list.md +110 -0
  40. package/skills/applying-slds/guidance/utilities/hyphenation.md +84 -0
  41. package/skills/applying-slds/guidance/utilities/index.md +205 -0
  42. package/skills/applying-slds/guidance/utilities/interactions.md +89 -0
  43. package/skills/applying-slds/guidance/utilities/layout.md +109 -0
  44. package/skills/applying-slds/guidance/utilities/line-clamp.md +131 -0
  45. package/skills/applying-slds/guidance/utilities/margin.md +155 -0
  46. package/skills/applying-slds/guidance/utilities/media-object.md +161 -0
  47. package/skills/applying-slds/guidance/utilities/name-value-list.md +152 -0
  48. package/skills/applying-slds/guidance/utilities/padding.md +155 -0
  49. package/skills/applying-slds/guidance/utilities/position.md +177 -0
  50. package/skills/applying-slds/guidance/utilities/print.md +114 -0
  51. package/skills/applying-slds/guidance/utilities/scrollable.md +126 -0
  52. package/skills/applying-slds/guidance/utilities/sizing.md +190 -0
  53. package/skills/applying-slds/guidance/utilities/themes.md +121 -0
  54. package/skills/applying-slds/guidance/utilities/truncate.md +127 -0
  55. package/skills/applying-slds/guidance/utilities/typography.md +166 -0
  56. package/skills/applying-slds/guidance/utilities/vertical-list.md +166 -0
  57. package/skills/applying-slds/guidance/utilities/visibility.md +228 -0
  58. package/skills/applying-slds/metadata/README.md +84 -0
  59. package/skills/applying-slds/metadata/blueprints/components/accordion.yaml +304 -0
  60. package/skills/applying-slds/metadata/blueprints/components/activity-timeline.yaml +92 -0
  61. package/skills/applying-slds/metadata/blueprints/components/alert.yaml +103 -0
  62. package/skills/applying-slds/metadata/blueprints/components/app-launcher.yaml +94 -0
  63. package/skills/applying-slds/metadata/blueprints/components/avatar-group.yaml +81 -0
  64. package/skills/applying-slds/metadata/blueprints/components/avatar.yaml +97 -0
  65. package/skills/applying-slds/metadata/blueprints/components/badges.yaml +102 -0
  66. package/skills/applying-slds/metadata/blueprints/components/brand-band.yaml +198 -0
  67. package/skills/applying-slds/metadata/blueprints/components/breadcrumbs.yaml +95 -0
  68. package/skills/applying-slds/metadata/blueprints/components/builder-header.yaml +192 -0
  69. package/skills/applying-slds/metadata/blueprints/components/button-groups.yaml +82 -0
  70. package/skills/applying-slds/metadata/blueprints/components/button-icons.yaml +295 -0
  71. package/skills/applying-slds/metadata/blueprints/components/buttons.yaml +230 -0
  72. package/skills/applying-slds/metadata/blueprints/components/cards.yaml +124 -0
  73. package/skills/applying-slds/metadata/blueprints/components/carousel.yaml +140 -0
  74. package/skills/applying-slds/metadata/blueprints/components/chat.yaml +179 -0
  75. package/skills/applying-slds/metadata/blueprints/components/checkbox-button-group.yaml +192 -0
  76. package/skills/applying-slds/metadata/blueprints/components/checkbox-button.yaml +204 -0
  77. package/skills/applying-slds/metadata/blueprints/components/checkbox-toggle.yaml +177 -0
  78. package/skills/applying-slds/metadata/blueprints/components/checkbox.yaml +108 -0
  79. package/skills/applying-slds/metadata/blueprints/components/color-picker.yaml +172 -0
  80. package/skills/applying-slds/metadata/blueprints/components/combobox.yaml +136 -0
  81. package/skills/applying-slds/metadata/blueprints/components/counter.yaml +147 -0
  82. package/skills/applying-slds/metadata/blueprints/components/data-tables.yaml +157 -0
  83. package/skills/applying-slds/metadata/blueprints/components/datepickers.yaml +130 -0
  84. package/skills/applying-slds/metadata/blueprints/components/datetime-picker.yaml +155 -0
  85. package/skills/applying-slds/metadata/blueprints/components/docked-composer.yaml +201 -0
  86. package/skills/applying-slds/metadata/blueprints/components/docked-form-footer.yaml +161 -0
  87. package/skills/applying-slds/metadata/blueprints/components/docked-utility-bar.yaml +175 -0
  88. package/skills/applying-slds/metadata/blueprints/components/drop-zone.yaml +115 -0
  89. package/skills/applying-slds/metadata/blueprints/components/dueling-picklist.yaml +196 -0
  90. package/skills/applying-slds/metadata/blueprints/components/dynamic-icons.yaml +128 -0
  91. package/skills/applying-slds/metadata/blueprints/components/dynamic-menu.yaml +141 -0
  92. package/skills/applying-slds/metadata/blueprints/components/expandable-section.yaml +115 -0
  93. package/skills/applying-slds/metadata/blueprints/components/expression.yaml +143 -0
  94. package/skills/applying-slds/metadata/blueprints/components/feeds.yaml +125 -0
  95. package/skills/applying-slds/metadata/blueprints/components/file-selector.yaml +154 -0
  96. package/skills/applying-slds/metadata/blueprints/components/files.yaml +119 -0
  97. package/skills/applying-slds/metadata/blueprints/components/form-element.yaml +145 -0
  98. package/skills/applying-slds/metadata/blueprints/components/global-header.yaml +120 -0
  99. package/skills/applying-slds/metadata/blueprints/components/global-navigation.yaml +100 -0
  100. package/skills/applying-slds/metadata/blueprints/components/icons.yaml +138 -0
  101. package/skills/applying-slds/metadata/blueprints/components/illustration.yaml +205 -0
  102. package/skills/applying-slds/metadata/blueprints/components/input.yaml +151 -0
  103. package/skills/applying-slds/metadata/blueprints/components/list-builder.yaml +127 -0
  104. package/skills/applying-slds/metadata/blueprints/components/lookups.yaml +132 -0
  105. package/skills/applying-slds/metadata/blueprints/components/map.yaml +118 -0
  106. package/skills/applying-slds/metadata/blueprints/components/menus.yaml +134 -0
  107. package/skills/applying-slds/metadata/blueprints/components/modals.yaml +152 -0
  108. package/skills/applying-slds/metadata/blueprints/components/notifications.yaml +88 -0
  109. package/skills/applying-slds/metadata/blueprints/components/page-headers.yaml +135 -0
  110. package/skills/applying-slds/metadata/blueprints/components/panels.yaml +149 -0
  111. package/skills/applying-slds/metadata/blueprints/components/path.yaml +154 -0
  112. package/skills/applying-slds/metadata/blueprints/components/picklist.yaml +125 -0
  113. package/skills/applying-slds/metadata/blueprints/components/pills.yaml +154 -0
  114. package/skills/applying-slds/metadata/blueprints/components/popovers.yaml +120 -0
  115. package/skills/applying-slds/metadata/blueprints/components/progress-bar.yaml +110 -0
  116. package/skills/applying-slds/metadata/blueprints/components/progress-indicator.yaml +133 -0
  117. package/skills/applying-slds/metadata/blueprints/components/progress-ring.yaml +102 -0
  118. package/skills/applying-slds/metadata/blueprints/components/prompt.yaml +126 -0
  119. package/skills/applying-slds/metadata/blueprints/components/publishers.yaml +178 -0
  120. package/skills/applying-slds/metadata/blueprints/components/radio-button-group.yaml +172 -0
  121. package/skills/applying-slds/metadata/blueprints/components/radio-group.yaml +112 -0
  122. package/skills/applying-slds/metadata/blueprints/components/rich-text-editor.yaml +135 -0
  123. package/skills/applying-slds/metadata/blueprints/components/scoped-notifications.yaml +188 -0
  124. package/skills/applying-slds/metadata/blueprints/components/scoped-tabs.yaml +97 -0
  125. package/skills/applying-slds/metadata/blueprints/components/select.yaml +127 -0
  126. package/skills/applying-slds/metadata/blueprints/components/setup-assistant.yaml +152 -0
  127. package/skills/applying-slds/metadata/blueprints/components/slider.yaml +111 -0
  128. package/skills/applying-slds/metadata/blueprints/components/spinners.yaml +135 -0
  129. package/skills/applying-slds/metadata/blueprints/components/split-view.yaml +112 -0
  130. package/skills/applying-slds/metadata/blueprints/components/summary-detail.yaml +103 -0
  131. package/skills/applying-slds/metadata/blueprints/components/tabs.yaml +138 -0
  132. package/skills/applying-slds/metadata/blueprints/components/textarea.yaml +116 -0
  133. package/skills/applying-slds/metadata/blueprints/components/tiles.yaml +108 -0
  134. package/skills/applying-slds/metadata/blueprints/components/timepicker.yaml +111 -0
  135. package/skills/applying-slds/metadata/blueprints/components/toast.yaml +154 -0
  136. package/skills/applying-slds/metadata/blueprints/components/tooltips.yaml +107 -0
  137. package/skills/applying-slds/metadata/blueprints/components/tree-grid.yaml +116 -0
  138. package/skills/applying-slds/metadata/blueprints/components/trees.yaml +116 -0
  139. package/skills/applying-slds/metadata/blueprints/components/trial-bar.yaml +112 -0
  140. package/skills/applying-slds/metadata/blueprints/components/vertical-navigation.yaml +130 -0
  141. package/skills/applying-slds/metadata/blueprints/components/vertical-tabs.yaml +140 -0
  142. package/skills/applying-slds/metadata/blueprints/components/visual-picker.yaml +150 -0
  143. package/skills/applying-slds/metadata/blueprints/components/welcome-mat.yaml +136 -0
  144. package/skills/applying-slds/metadata/hooks-index.json +6272 -0
  145. package/skills/applying-slds/metadata/icon-metadata.json +38466 -0
  146. package/skills/applying-slds/metadata/utilities-index.json +21912 -0
  147. package/skills/applying-slds/references/component-selection.md +112 -0
  148. package/skills/applying-slds/references/icons-decision-guide.md +124 -0
  149. package/skills/applying-slds/references/styling-decision-guide.md +228 -0
  150. package/skills/applying-slds/references/utilities-quick-ref.md +125 -0
  151. package/skills/applying-slds/scripts/search-blueprints.cjs +117 -0
  152. package/skills/applying-slds/scripts/search-hooks.cjs +139 -0
  153. package/skills/applying-slds/scripts/search-icons.cjs +174 -0
  154. package/skills/applying-slds/scripts/search-utilities.cjs +161 -0
  155. package/skills/building-mobile-apps/SKILL.md +0 -1
  156. package/skills/building-omnistudio-callable-apex/SKILL.md +0 -1
  157. package/skills/building-omnistudio-datamapper/SKILL.md +0 -1
  158. package/skills/building-omnistudio-flexcard/SKILL.md +0 -1
  159. package/skills/building-omnistudio-integration-procedure/SKILL.md +0 -1
  160. package/skills/building-omnistudio-omniscript/SKILL.md +0 -1
  161. package/skills/building-sf-integrations/SKILL.md +0 -1
  162. package/skills/configuring-connected-apps/SKILL.md +0 -1
  163. package/skills/connecting-datacloud/SKILL.md +0 -1
  164. package/skills/creating-b2b-commerce-store/SKILL.md +0 -1
  165. package/skills/debugging-apex-logs/SKILL.md +0 -1
  166. package/skills/deploying-metadata/SKILL.md +0 -1
  167. package/skills/deploying-omnistudio-datapacks/SKILL.md +0 -1
  168. package/skills/developing-agentforce/SKILL.md +0 -1
  169. package/skills/fetching-salesforce-docs/SKILL.md +0 -1
  170. package/skills/generating-custom-lightning-type/SKILL.md +17 -39
  171. package/skills/generating-custom-lightning-type/assets/primitive-types-and-constraints.md +41 -0
  172. package/skills/generating-custom-lightning-type/references/widget-rendition.md +124 -0
  173. package/skills/generating-lwc-components/SKILL.md +0 -1
  174. package/skills/generating-mermaid-diagrams/SKILL.md +0 -1
  175. package/skills/generating-visual-diagrams/SKILL.md +0 -1
  176. package/skills/handling-sf-data/SKILL.md +0 -1
  177. package/skills/harmonizing-datacloud/SKILL.md +0 -1
  178. package/skills/integrating-b2b-commerce-open-code-components/SKILL.md +0 -1
  179. package/skills/investigating-agentforce-architecture/README.md +156 -0
  180. package/skills/investigating-agentforce-architecture/SKILL.md +230 -0
  181. package/skills/investigating-agentforce-architecture/assets/cli/describe_sobject.yaml +16 -0
  182. package/skills/investigating-agentforce-architecture/assets/cli/describe_tooling_sobject.yaml +17 -0
  183. package/skills/investigating-agentforce-architecture/assets/cli/list_metadata_genaiprompttemplate.yaml +17 -0
  184. package/skills/investigating-agentforce-architecture/assets/cli/org_display.yaml +15 -0
  185. package/skills/investigating-agentforce-architecture/assets/cli/retrieve_genai_plugin.yaml +18 -0
  186. package/skills/investigating-agentforce-architecture/assets/cli/show_access_token.yaml +27 -0
  187. package/skills/investigating-agentforce-architecture/assets/mermaid/action_tree.mmd +20 -0
  188. package/skills/investigating-agentforce-architecture/assets/mermaid/data_flow.mmd +19 -0
  189. package/skills/investigating-agentforce-architecture/assets/mermaid/dependency_graph.mmd +19 -0
  190. package/skills/investigating-agentforce-architecture/assets/mermaid/invocation_sequence.mmd +20 -0
  191. package/skills/investigating-agentforce-architecture/assets/mermaid/planner_state.mmd +18 -0
  192. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_ids.soql +3 -0
  193. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_names.soql +3 -0
  194. package/skills/investigating-agentforce-architecture/assets/soql/bot_definition_details.soql +3 -0
  195. package/skills/investigating-agentforce-architecture/assets/soql/bot_version_lookup.soql +4 -0
  196. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_by_ids.soql +3 -0
  197. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_ids_by_names.soql +3 -0
  198. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_view_by_durable_ids.soql +4 -0
  199. package/skills/investigating-agentforce-architecture/assets/soql/flow_metadata_by_id.soql +3 -0
  200. package/skills/investigating-agentforce-architecture/assets/soql/functions_by_plugins.soql +5 -0
  201. package/skills/investigating-agentforce-architecture/assets/soql/planner_attrs_by_parent_ids.soql +3 -0
  202. package/skills/investigating-agentforce-architecture/assets/soql/planner_bundle_functions.soql +3 -0
  203. package/skills/investigating-agentforce-architecture/assets/soql/planner_definition_by_agent_chain.soql +3 -0
  204. package/skills/investigating-agentforce-architecture/assets/soql/plugin_functions_by_plugin_ids.soql +3 -0
  205. package/skills/investigating-agentforce-architecture/assets/soql/plugin_instructions_by_plugin_ids.soql +3 -0
  206. package/skills/investigating-agentforce-architecture/assets/soql/plugins_by_planner.soql +4 -0
  207. package/skills/investigating-agentforce-architecture/references/architecture_sections.md +243 -0
  208. package/skills/investigating-agentforce-architecture/references/contract.json +244 -0
  209. package/skills/investigating-agentforce-architecture/references/soql_fields.md +512 -0
  210. package/skills/investigating-agentforce-architecture/scripts/_shared/__init__.py +1 -0
  211. package/skills/investigating-agentforce-architecture/scripts/_shared/fs_guard.py +329 -0
  212. package/skills/investigating-agentforce-architecture/scripts/_shared/paths.py +110 -0
  213. package/skills/investigating-agentforce-architecture/scripts/_shared/runtime.py +59 -0
  214. package/skills/investigating-agentforce-architecture/scripts/_shared/sql.py +10 -0
  215. package/skills/investigating-agentforce-architecture/scripts/cache_check.py +234 -0
  216. package/skills/investigating-agentforce-architecture/scripts/config.py +131 -0
  217. package/skills/investigating-agentforce-architecture/scripts/fetch_soql.py +689 -0
  218. package/skills/investigating-agentforce-architecture/scripts/finalize.py +295 -0
  219. package/skills/investigating-agentforce-architecture/scripts/main.py +2835 -0
  220. package/skills/investigating-agentforce-architecture/scripts/metadata_listing.py +265 -0
  221. package/skills/investigating-agentforce-architecture/scripts/parallel_retrieve.py +69 -0
  222. package/skills/investigating-agentforce-architecture/scripts/parse_bundle.py +215 -0
  223. package/skills/investigating-agentforce-architecture/scripts/parse_wave.py +845 -0
  224. package/skills/investigating-agentforce-architecture/scripts/probe_channels.py +302 -0
  225. package/skills/investigating-agentforce-architecture/scripts/render_architecture.py +1043 -0
  226. package/skills/investigating-agentforce-architecture/scripts/resolve_bot.py +255 -0
  227. package/skills/investigating-agentforce-architecture/scripts/resolve_invocation_target.py +130 -0
  228. package/skills/investigating-agentforce-architecture/scripts/rest_client.py +763 -0
  229. package/skills/investigating-agentforce-architecture/scripts/retrieve_planner.py +13 -0
  230. package/skills/investigating-agentforce-architecture/scripts/sf_cli.py +242 -0
  231. package/skills/investigating-agentforce-architecture/scripts/soql_loader.py +253 -0
  232. package/skills/investigating-agentforce-architecture/scripts/summarize_tree.py +143 -0
  233. package/skills/investigating-agentforce-architecture/scripts/tests/__init__.py +0 -0
  234. package/skills/investigating-agentforce-architecture/scripts/tests/_bootstrap.py +23 -0
  235. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/__init__.py +0 -0
  236. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/genai_payloads.py +400 -0
  237. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check.py +307 -0
  238. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check_main.py +283 -0
  239. package/skills/investigating-agentforce-architecture/scripts/tests/test_config.py +115 -0
  240. package/skills/investigating-agentforce-architecture/scripts/tests/test_end_to_end_fixture.py +651 -0
  241. package/skills/investigating-agentforce-architecture/scripts/tests/test_finalize.py +278 -0
  242. package/skills/investigating-agentforce-architecture/scripts/tests/test_flow_children_inflation.py +582 -0
  243. package/skills/investigating-agentforce-architecture/scripts/tests/test_fs_guard.py +113 -0
  244. package/skills/investigating-agentforce-architecture/scripts/tests/test_iterative_wave_b.py +478 -0
  245. package/skills/investigating-agentforce-architecture/scripts/tests/test_main_pipeline.py +3359 -0
  246. package/skills/investigating-agentforce-architecture/scripts/tests/test_parallel_retrieve.py +131 -0
  247. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_bundle.py +400 -0
  248. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave.py +644 -0
  249. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py +224 -0
  250. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_helpers.py +380 -0
  251. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_main.py +397 -0
  252. package/skills/investigating-agentforce-architecture/scripts/tests/test_per_branch_visited.py +244 -0
  253. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_channels.py +359 -0
  254. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_cli_recipes.py +185 -0
  255. package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py +810 -0
  256. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_bot.py +203 -0
  257. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_creds.py +157 -0
  258. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_invocation_target.py +145 -0
  259. package/skills/investigating-agentforce-architecture/scripts/tests/test_rest_client.py +1253 -0
  260. package/skills/investigating-agentforce-architecture/scripts/tests/test_runtime_override.py +100 -0
  261. package/skills/investigating-agentforce-architecture/scripts/tests/test_sf_cli.py +261 -0
  262. package/skills/investigating-agentforce-architecture/scripts/tests/test_signature_stamping.py +466 -0
  263. package/skills/investigating-agentforce-architecture/scripts/tests/test_soql_loader.py +501 -0
  264. package/skills/investigating-agentforce-architecture/scripts/tests/test_summarize_tree.py +241 -0
  265. package/skills/investigating-agentforce-architecture/scripts/tests/test_write_emit_ctx.py +480 -0
  266. package/skills/investigating-agentforce-architecture/tools/emit_env.py +157 -0
  267. package/skills/investigating-agentforce-architecture/tools/emit_result.py +262 -0
  268. package/skills/investigating-agentforce-architecture/tools/sanitize.py +33 -0
  269. package/skills/investigating-agentforce-architecture/tools/write_emit_ctx.py +332 -0
  270. package/skills/investigating-agentforce-d360/README.md +123 -0
  271. package/skills/investigating-agentforce-d360/SKILL.md +163 -0
  272. package/skills/investigating-agentforce-d360/assets/dc/app_generation.sql +51 -0
  273. package/skills/investigating-agentforce-d360/assets/dc/content_category.sql +44 -0
  274. package/skills/investigating-agentforce-d360/assets/dc/content_quality.sql +41 -0
  275. package/skills/investigating-agentforce-d360/assets/dc/discover_sessions.sql +36 -0
  276. package/skills/investigating-agentforce-d360/assets/dc/feedback.sql +47 -0
  277. package/skills/investigating-agentforce-d360/assets/dc/feedback_details.sql +38 -0
  278. package/skills/investigating-agentforce-d360/assets/dc/gateway_records.sql +45 -0
  279. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_llm.sql +50 -0
  280. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_metadata.sql +44 -0
  281. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_tags.sql +42 -0
  282. package/skills/investigating-agentforce-d360/assets/dc/gateway_requests.sql +89 -0
  283. package/skills/investigating-agentforce-d360/assets/dc/gateway_responses.sql +43 -0
  284. package/skills/investigating-agentforce-d360/assets/dc/generations.sql +52 -0
  285. package/skills/investigating-agentforce-d360/assets/dc/interactions.sql +53 -0
  286. package/skills/investigating-agentforce-d360/assets/dc/messages.sql +53 -0
  287. package/skills/investigating-agentforce-d360/assets/dc/messaging_session.sql +37 -0
  288. package/skills/investigating-agentforce-d360/assets/dc/moment_interactions.sql +34 -0
  289. package/skills/investigating-agentforce-d360/assets/dc/moments.sql +39 -0
  290. package/skills/investigating-agentforce-d360/assets/dc/participants.sql +48 -0
  291. package/skills/investigating-agentforce-d360/assets/dc/sessions.sql +78 -0
  292. package/skills/investigating-agentforce-d360/assets/dc/steps.sql +64 -0
  293. package/skills/investigating-agentforce-d360/assets/dc/tag_associations.sql +46 -0
  294. package/skills/investigating-agentforce-d360/assets/dc/tag_definition_associations.sql +37 -0
  295. package/skills/investigating-agentforce-d360/assets/dc/tag_definitions.sql +50 -0
  296. package/skills/investigating-agentforce-d360/assets/dc/tags.sql +37 -0
  297. package/skills/investigating-agentforce-d360/assets/dc/telemetry_spans.sql +55 -0
  298. package/skills/investigating-agentforce-d360/references/artifacts.md +50 -0
  299. package/skills/investigating-agentforce-d360/references/dc_dmo_fields.md +823 -0
  300. package/skills/investigating-agentforce-d360/references/dc_pipeline_contract.md +608 -0
  301. package/skills/investigating-agentforce-d360/scripts/_shared/__init__.py +2 -0
  302. package/skills/investigating-agentforce-d360/scripts/_shared/cli_override.py +98 -0
  303. package/skills/investigating-agentforce-d360/scripts/_shared/fs_guard.py +334 -0
  304. package/skills/investigating-agentforce-d360/scripts/_shared/paths.py +155 -0
  305. package/skills/investigating-agentforce-d360/scripts/_shared/runtime.py +59 -0
  306. package/skills/investigating-agentforce-d360/scripts/_shared/sql.py +14 -0
  307. package/skills/investigating-agentforce-d360/scripts/assemble_dc.py +1624 -0
  308. package/skills/investigating-agentforce-d360/scripts/config.py +45 -0
  309. package/skills/investigating-agentforce-d360/scripts/dc.py +188 -0
  310. package/skills/investigating-agentforce-d360/scripts/discover_sessions.py +556 -0
  311. package/skills/investigating-agentforce-d360/scripts/fetch_dc.py +1045 -0
  312. package/skills/investigating-agentforce-d360/scripts/render_dc.py +1750 -0
  313. package/skills/investigating-agentforce-d360/scripts/resolve_session.py +264 -0
  314. package/skills/investigating-agentforce-d360/scripts/storage.py +92 -0
  315. package/skills/investigating-agentforce-d360/scripts/tests/__init__.py +0 -0
  316. package/skills/investigating-agentforce-d360/scripts/tests/_bootstrap.py +15 -0
  317. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/__init__.py +0 -0
  318. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/synthetic_session.py +424 -0
  319. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_bootstrap_and_mode.py +115 -0
  320. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct.py +220 -0
  321. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct_integration.py +158 -0
  322. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_helpers.py +287 -0
  323. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_integration.py +247 -0
  324. package/skills/investigating-agentforce-d360/scripts/tests/test_dc_and_resolve_session.py +433 -0
  325. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions.py +458 -0
  326. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions_grep_ci.py +193 -0
  327. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_helpers.py +266 -0
  328. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_identity.py +528 -0
  329. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_main.py +251 -0
  330. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall.py +229 -0
  331. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall_full.py +283 -0
  332. package/skills/investigating-agentforce-d360/scripts/tests/test_identity_coherence.py +327 -0
  333. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_branches.py +256 -0
  334. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_gateway_direct.py +130 -0
  335. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_helpers.py +291 -0
  336. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_integration.py +220 -0
  337. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_planner_llm_calls.py +284 -0
  338. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_show_prompts_gating.py +215 -0
  339. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_from_disk.py +100 -0
  340. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_session_main.py +149 -0
  341. package/skills/investigating-agentforce-d360/scripts/tests/test_runtime_override.py +104 -0
  342. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape.py +95 -0
  343. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape_dropped_by_stdm.py +85 -0
  344. package/skills/managing-managed-event-subscription/SKILL.md +152 -0
  345. package/skills/managing-managed-event-subscription/assets/managed-event-subscription-template.xml +20 -0
  346. package/skills/managing-managed-event-subscription/references/delete-guide.md +57 -0
  347. package/skills/managing-managed-event-subscription/references/topic-name-formats.md +26 -0
  348. package/skills/managing-managed-event-subscription/references/update-constraints.md +30 -0
  349. package/skills/modeling-omnistudio-epc-catalog/SKILL.md +0 -1
  350. package/skills/observing-agentforce/SKILL.md +0 -1
  351. package/skills/orchestrating-datacloud/SKILL.md +0 -1
  352. package/skills/preparing-datacloud/SKILL.md +0 -1
  353. package/skills/querying-soql/SKILL.md +0 -1
  354. package/skills/retrieving-datacloud/SKILL.md +0 -1
  355. package/skills/running-apex-tests/SKILL.md +0 -1
  356. package/skills/running-code-analyzer/SKILL.md +0 -1
  357. package/skills/segmenting-datacloud/SKILL.md +0 -1
  358. package/skills/testing-agentforce/SKILL.md +0 -1
  359. package/skills/uplifting-components-to-slds2/SKILL.md +3 -2
  360. package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +30 -9
  361. package/skills/uplifting-components-to-slds2/references/examples.md +24 -6
  362. package/skills/validating-slds/SKILL.md +262 -0
  363. package/skills/validating-slds/references/quality-checks.md +308 -0
  364. package/skills/validating-slds/references/report-format.md +302 -0
  365. 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 &amp; 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())