@salesforce/afv-skills 1.30.0 → 1.32.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 (126) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-bot-upgrade/SKILL.md +217 -0
  3. package/skills/agentforce-bot-upgrade/references/extraction-blueprint.md +242 -0
  4. package/skills/agentforce-bot-upgrade/references/generate-agent-spec.md +128 -0
  5. package/skills/agentforce-bot-upgrade/references/handoff-output-format.md +208 -0
  6. package/skills/agentforce-bot-upgrade/references/input-contract.md +101 -0
  7. package/skills/agentforce-bot-upgrade/references/mapping-rules.md +113 -0
  8. package/skills/agentforce-bot-upgrade/references/planner-workflow-reference.md +78 -0
  9. package/skills/agentforce-bot-upgrade/references/post-conversion-enhancements-reference.md +67 -0
  10. package/skills/agentforce-bot-upgrade/references/quality-checklist.md +33 -0
  11. package/skills/agentforce-bot-upgrade/references/sf-cli-bot-reference.md +116 -0
  12. package/skills/data360-code-extension-generate/SKILL.md +15 -2
  13. package/skills/dx-devops-work-item-manage/SKILL.md +270 -0
  14. package/skills/dx-devops-work-item-manage/examples/common-workflows.md +479 -0
  15. package/skills/dx-devops-work-item-manage/references/cli-commands.md +476 -0
  16. package/skills/dx-org-manage/SKILL.md +8 -5
  17. package/skills/dx-org-trial-expiration-check/SKILL.md +239 -0
  18. package/skills/dx-org-trial-expiration-check/scripts/check_expiration.sh +588 -0
  19. package/skills/dx-pkg-post-install-configure/SKILL.md +184 -0
  20. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +202 -35
  21. package/skills/experience-ui-bundle-app-coordinate/scripts/check-hosting-target.sh +26 -0
  22. package/skills/experience-ui-bundle-app-coordinate/scripts/check-sfdx-project.sh +15 -0
  23. package/skills/experience-ui-bundle-custom-app-generate/SKILL.md +29 -5
  24. package/skills/experience-ui-bundle-custom-app-generate/references/configure-metadata-custom-application.md +1 -1
  25. package/skills/experience-ui-bundle-custom-app-generate/scripts/resolve-uibundle-path.sh +30 -0
  26. package/skills/experience-ui-bundle-features-generate/SKILL.md +86 -11
  27. package/skills/experience-ui-bundle-features-generate/references/conflict-resolution-schema.json +20 -0
  28. package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +14 -0
  29. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +6 -1
  30. package/skills/experience-ui-bundle-frontend-generate/references/component.md +34 -1
  31. package/skills/experience-ui-bundle-frontend-generate/references/page.md +1 -1
  32. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +19 -8
  33. package/skills/experience-ui-bundle-metadata-generate/references/csp-metadata-format.md +2 -2
  34. package/skills/experience-ui-bundle-metadata-generate/scripts/check-api-version.sh +30 -0
  35. package/skills/experience-ui-bundle-project-generate/SKILL.md +111 -0
  36. package/skills/experience-ui-bundle-project-generate/scripts/flatten-project.mjs +33 -0
  37. package/skills/platform-custom-field-generate/SKILL.md +12 -11
  38. package/skills/platform-custom-field-generate/references/advanced-picklists.md +17 -17
  39. package/skills/platform-custom-lightning-type-generate/SKILL.md +9 -14
  40. package/skills/platform-custom-lightning-type-generate/references/widget-rendition.md +27 -111
  41. package/skills/platform-custom-report-type-generate/SKILL.md +259 -0
  42. package/skills/platform-custom-report-type-generate/examples/AccountProjectsWithTasks.reportType-meta.xml +51 -0
  43. package/skills/platform-custom-report-type-generate/examples/AccountsWithIndustry.reportType-meta.xml +27 -0
  44. package/skills/platform-custom-report-type-generate/examples/AccountsWithProjects.reportType-meta.xml +44 -0
  45. package/skills/platform-custom-report-type-generate/references/category-values.md +32 -0
  46. package/skills/platform-custom-report-type-generate/references/errors-and-troubleshooting.md +24 -0
  47. package/skills/platform-dataspace-access-configure/SKILL.md +318 -0
  48. package/skills/platform-docs-get/SKILL.md +4 -0
  49. package/skills/platform-docs-get/scripts/extract_salesforce_doc.py +78 -13
  50. package/skills/platform-encryption-configure/SKILL.md +122 -0
  51. package/skills/platform-encryption-configure/assets/EncryptionKey.settings-meta.xml +17 -0
  52. package/skills/platform-encryption-configure/assets/PlatformEncryption.settings-meta.xml +15 -0
  53. package/skills/platform-encryption-configure/assets/encrypted-field.field-meta.xml +22 -0
  54. package/skills/platform-encryption-configure/examples/cache-only-keys.settings-meta.xml +11 -0
  55. package/skills/platform-encryption-configure/references/encryption-schemes.md +34 -0
  56. package/skills/platform-encryption-configure/references/key-models.md +34 -0
  57. package/skills/platform-encryption-configure/references/tenant-secret-lifecycle.md +23 -0
  58. package/skills/platform-encryption-configure/scripts/validate-encryption-metadata.sh +54 -0
  59. package/skills/platform-lightning-type-widget-coordinate/SKILL.md +214 -0
  60. package/skills/platform-lightning-type-widget-coordinate/examples/existing-lightning-type-with-widget-prompt.md +80 -0
  61. package/skills/platform-lightning-type-widget-coordinate/examples/new-lightning-type-with-widget-prompt.md +75 -0
  62. package/skills/platform-lightning-type-widget-coordinate/references/build-plan-format.md +67 -0
  63. package/skills/platform-lightning-type-widget-coordinate/references/lightning-type-discovery.md +124 -0
  64. package/skills/platform-lightning-type-widget-coordinate/references/validation-gates.md +130 -0
  65. package/skills/platform-metadata-api-context-get/SKILL.md +13 -10
  66. package/skills/platform-sharing-owd-configure/SKILL.md +142 -0
  67. package/skills/platform-sharing-owd-configure/examples/get_owd_output.md +38 -0
  68. package/skills/platform-sharing-owd-configure/examples/update_owd_output.md +44 -0
  69. package/skills/platform-sharing-owd-configure/references/access_levels.md +49 -0
  70. package/skills/platform-sharing-owd-configure/references/metadata_api_approach.md +69 -0
  71. package/skills/platform-sharing-rules-generate/SKILL.md +89 -19
  72. package/skills/platform-sharing-rules-generate/examples/test-cases.md +381 -0
  73. package/skills/platform-widget-generate/SKILL.md +241 -0
  74. package/skills/platform-widget-generate/examples/conditional.json +73 -0
  75. package/skills/platform-widget-generate/examples/list-with-foreach.json +66 -0
  76. package/skills/platform-widget-generate/examples/single-object.json +64 -0
  77. package/skills/platform-widget-generate/references/schema-from-lightning-type.md +76 -0
  78. package/skills/platform-widget-generate/references/widget-bundle-layout.md +116 -0
  79. package/skills/platform-widget-generate/references/widget-meta-directives.md +130 -0
  80. package/skills/sales-agentforce-pipeline-management-configure/SKILL.md +268 -0
  81. package/skills/sales-agentforce-pipeline-management-configure/assets/field-completion-template.genAiPromptTemplate-meta.xml +141 -0
  82. package/skills/sales-agentforce-pipeline-management-configure/assets/pipeline_management_flow.flow-meta.xml +548 -0
  83. package/skills/sales-agentforce-pipeline-management-configure/assets/sales_management_agent.agent +2938 -0
  84. package/skills/sales-agentforce-pipeline-management-configure/assets/sales_management_agent.bundle-meta.xml +4 -0
  85. package/skills/sales-agentforce-pipeline-management-configure/assets/sfdx-project.json +12 -0
  86. package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/bant.txt +11 -0
  87. package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/meddic.txt +11 -0
  88. package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/spiced.txt +11 -0
  89. package/skills/sales-agentforce-pipeline-management-configure/examples/custom-prompt-instructions.md +317 -0
  90. package/skills/sales-agentforce-pipeline-management-configure/references/admin-communication.md +355 -0
  91. package/skills/sales-agentforce-pipeline-management-configure/references/agent-creation.md +383 -0
  92. package/skills/sales-agentforce-pipeline-management-configure/references/auth-and-cli.md +146 -0
  93. package/skills/sales-agentforce-pipeline-management-configure/references/automation-matrix.md +23 -0
  94. package/skills/sales-agentforce-pipeline-management-configure/references/autonomous-updates.md +215 -0
  95. package/skills/sales-agentforce-pipeline-management-configure/references/canonical-agent-path.md +75 -0
  96. package/skills/sales-agentforce-pipeline-management-configure/references/data-sources.md +155 -0
  97. package/skills/sales-agentforce-pipeline-management-configure/references/field-completion-prompt-template.md +120 -0
  98. package/skills/sales-agentforce-pipeline-management-configure/references/field-completion-prompts.md +149 -0
  99. package/skills/sales-agentforce-pipeline-management-configure/references/flags.md +13 -0
  100. package/skills/sales-agentforce-pipeline-management-configure/references/flow-clone-from-template.md +447 -0
  101. package/skills/sales-agentforce-pipeline-management-configure/references/metadata-inventory.md +330 -0
  102. package/skills/sales-agentforce-pipeline-management-configure/references/opportunity-stages.md +246 -0
  103. package/skills/sales-agentforce-pipeline-management-configure/references/repair-diagnostics.md +265 -0
  104. package/skills/sales-agentforce-pipeline-management-configure/references/setup-order.md +263 -0
  105. package/skills/sales-agentforce-pipeline-management-configure/references/soap-api-enablement.md +528 -0
  106. package/skills/sales-agentforce-pipeline-management-configure/scripts/CHANGES.md +180 -0
  107. package/skills/sales-agentforce-pipeline-management-configure/scripts/README.md +56 -0
  108. package/skills/sales-agentforce-pipeline-management-configure/scripts/add-field-suggestion.sh +679 -0
  109. package/skills/sales-agentforce-pipeline-management-configure/scripts/create-agent.sh +302 -0
  110. package/skills/sales-agentforce-pipeline-management-configure/scripts/create-flow.sh +250 -0
  111. package/skills/sales-agentforce-pipeline-management-configure/scripts/define-agent-access.sh +430 -0
  112. package/skills/sales-agentforce-pipeline-management-configure/scripts/deploy-settings.sh +77 -0
  113. package/skills/sales-agentforce-pipeline-management-configure/scripts/enable-deal-agent.sh +241 -0
  114. package/skills/sales-agentforce-pipeline-management-configure/scripts/enable-prerequisites.sh +538 -0
  115. package/skills/sales-agentforce-pipeline-management-configure/scripts/flow-debug-and-verify.sh +385 -0
  116. package/skills/sales-agentforce-pipeline-management-configure/scripts/retrieve-settings.sh +131 -0
  117. package/skills/sales-agentforce-pipeline-management-configure/scripts/setup-all.sh +2791 -0
  118. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/agent-bundle-publish.sh +189 -0
  119. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/agent-detection.sh +239 -0
  120. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/flow-builder.sh +238 -0
  121. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/psg.sh +48 -0
  122. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/soap.sh +140 -0
  123. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/stage-descriptions.sh +448 -0
  124. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/test-opp.sh +166 -0
  125. package/skills/sales-agentforce-pipeline-management-configure/scripts/verify-all.sh +636 -0
  126. package/skills/sales-agentforce-pipeline-management-configure/scripts/verify-prompt-generation.sh +346 -0
@@ -0,0 +1,430 @@
1
+ #!/bin/bash
2
+
3
+ # Define Agent Access for the Pipeline Management Sales Agent (W-23242378)
4
+ #
5
+ # PROBLEM this solves:
6
+ # When the Pipeline Management Sales Agent (a BotDefinition matched by
7
+ # AgentTemplate — SalesMgmt__NGASalesAgent / SalesMgmt__SalesAgent) is created,
8
+ # it is NOT added to the "Agent Access" section of any permission set that
9
+ # human users hold. Users assigned the managed PSG `SalesManagementUserPsg`
10
+ # therefore cannot launch or chat with the agent, even though the agent is
11
+ # active and the PSG is assigned.
12
+ #
13
+ # WHY it can't be done directly on the PSG:
14
+ # `SalesManagementUserPsg` is a MANAGED permission set group — its Agent Access
15
+ # cannot be edited. Agent Access (SetupEntityAccess with SetupEntityType=
16
+ # 'BotDefinition') can only live on a CUSTOM permission set (License=None).
17
+ #
18
+ # THE FIX (mirrors the manual Setup UI flow):
19
+ # 1. Create a CUSTOM permission set `Sales_Agent_Access` (License=None).
20
+ # 2. Add the agent to its Agent Access: insert SetupEntityAccess
21
+ # (ParentId=<permset Id>, SetupEntityId=<BotDefinition Id>). Standard Data API.
22
+ # SetupEntityType is derived — do NOT set it on insert. Requires API v64.0+.
23
+ # 3. Add the custom permset to `SalesManagementUserPsg` via
24
+ # PermissionSetGroupComponent (Tooling API object — needs --use-tooling-api).
25
+ # 4. Confirm the PSG recalculates (the component insert triggers async recalc;
26
+ # poll Status until 'Updated'). => Agent becomes available to all users
27
+ # holding SalesManagementUserPsg.
28
+ #
29
+ # The BotDefinition is detected by AgentTemplate (via shared/agent-detection.sh),
30
+ # NOT by a hardcoded DeveloperName, so it matches whatever local name the agent
31
+ # was published with (SalesAgent, Sale_Agent, etc.) — the same logic the
32
+ # scheduled flow uses.
33
+ #
34
+ # This script is idempotent (query-then-skip; SetupEntityAccess has no Update)
35
+ # and safe to re-run. It must run AFTER the agent (BotDefinition) exists.
36
+ #
37
+ # Usage: ./define-agent-access.sh <org-alias>
38
+
39
+ set -euo pipefail
40
+
41
+ # sf CLI can emit ANSI color codes inside --json stdout, breaking jq parsing.
42
+ # 2>/dev/null only strips stderr; these env vars are the reliable fix (see PM notes).
43
+ export NO_COLOR=1
44
+ export FORCE_COLOR=0
45
+
46
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
47
+
48
+ # Shared PM bot detection (AgentTemplate-based). Provides PM_AGENT_TEMPLATES_SOQL
49
+ # and pm_bot_developer_name().
50
+ source "$SCRIPT_DIR/shared/agent-detection.sh"
51
+ # Shared PSG recalculation status reader (psg_status).
52
+ source "$SCRIPT_DIR/shared/psg.sh"
53
+
54
+ ORG_ALIAS="${1:-}"
55
+
56
+ # --- Helpers (mirror setup-all.sh conventions) ---
57
+ log_pass() { echo " [PASS] $1"; }
58
+ log_warn() { echo " [WARN] $1"; }
59
+ log_fail() { echo " [FAIL] $1"; }
60
+ log_info() { echo " [....] $1"; }
61
+ log_try() { echo " [TRY ] $1"; }
62
+
63
+ if [[ -z "$ORG_ALIAS" ]]; then
64
+ echo "Error: Missing org alias"
65
+ echo "Usage: $0 <org-alias>"
66
+ exit 1
67
+ fi
68
+
69
+ # Validate org alias (prevent shell injection via metacharacters), matching
70
+ # the pattern used by setup-all.sh and verify-all.sh.
71
+ if [[ ! "$ORG_ALIAS" =~ ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]]; then
72
+ echo "ERROR: Invalid org alias. Only alphanumeric, dots, hyphens, and underscores allowed (must start with alphanumeric)."
73
+ exit 1
74
+ fi
75
+
76
+ # Preflight: this script parses every CLI response with jq and calls the sf CLI,
77
+ # and SetupEntityAccess for BotDefinition needs the sf CLI's default API (v64.0+,
78
+ # shipped by sf CLI v2.x). Fail fast with an actionable message instead of a
79
+ # cryptic mid-run error.
80
+ if ! command -v jq >/dev/null 2>&1; then
81
+ echo "ERROR: 'jq' is required but not installed. Install it (e.g. 'brew install jq') and re-run."
82
+ exit 1
83
+ fi
84
+ if ! command -v sf >/dev/null 2>&1; then
85
+ echo "ERROR: Salesforce CLI ('sf') is required but not installed. Install the v2.x 'sf' CLI and re-run."
86
+ exit 1
87
+ fi
88
+ SF_MAJOR=$(sf --version 2>/dev/null | grep -oE '@salesforce/cli/[0-9]+' | grep -oE '[0-9]+$' || echo "")
89
+ if [[ -n "$SF_MAJOR" && "$SF_MAJOR" -lt 2 ]]; then
90
+ echo "ERROR: Salesforce CLI v2.x or newer is required (found major version ${SF_MAJOR})."
91
+ echo " Agent Access (SetupEntityAccess for BotDefinition) needs API v64.0+, which ships with sf CLI v2.x."
92
+ exit 1
93
+ fi
94
+
95
+ # Configurable identifiers
96
+ ACCESS_PS_NAME="Sales_Agent_Access"
97
+ ACCESS_PS_LABEL="Sales Agent Access"
98
+ USER_PSG_NAME="SalesManagementUserPsg"
99
+ AGENT_PSG_NAME="SalesManagementAgentUserPsg"
100
+
101
+ echo "=== Define Agent Access for Pipeline Management Sales Agent ==="
102
+ echo ""
103
+
104
+ # ------------------------------------------------------------
105
+ # Step 0: The agent must exist first (BotDefinition Id is runtime-only).
106
+ # Detect it by AgentTemplate, matching the scheduled flow's own lookup — not by
107
+ # a hardcoded DeveloperName. Uses a TWO-STEP query: first find the BotDefinition
108
+ # by AgentTemplate (no subquery), then separately confirm it has an Active
109
+ # BotVersion. The single-query `Id IN (SELECT ... FROM BotVersion WHERE Status =
110
+ # 'Active')` form was removed in de77d520 because that correlated subquery hangs
111
+ # 60+ seconds on some orgs.
112
+ # ------------------------------------------------------------
113
+ BOT_DEF=$(sf data query \
114
+ --query "SELECT Id, DeveloperName FROM BotDefinition WHERE AgentTemplate IN (${PM_AGENT_TEMPLATES_SOQL}) ORDER BY LastModifiedDate DESC LIMIT 1" \
115
+ --target-org "$ORG_ALIAS" \
116
+ --json 2>/dev/null | jq -r '.result.records[0] // empty' 2>/dev/null || echo "")
117
+
118
+ BOT_DEF_ID=$(echo "$BOT_DEF" | jq -r '.Id // empty' 2>/dev/null || echo "")
119
+ BOT_DEF_NAME=$(echo "$BOT_DEF" | jq -r '.DeveloperName // empty' 2>/dev/null || echo "")
120
+
121
+ # BOT_DEF_ID is interpolated into later SOQL; reject anything that is not a
122
+ # valid Salesforce record Id (15 or 18 char alphanumeric).
123
+ if [[ -n "$BOT_DEF_ID" && ! "$BOT_DEF_ID" =~ ^[a-zA-Z0-9]{15,18}$ ]]; then
124
+ log_fail "Invalid BotDefinition ID format: $BOT_DEF_ID"
125
+ exit 1
126
+ fi
127
+
128
+ if [[ -z "$BOT_DEF_ID" ]]; then
129
+ log_warn "No Pipeline Management BotDefinition found — cannot define Agent Access yet."
130
+ echo " Agent Access can only be defined after the agent exists and has an Active version."
131
+ echo " Create the agent first (./create-agent.sh $ORG_ALIAS), then re-run this script."
132
+ exit 0
133
+ fi
134
+
135
+ # Separately confirm the bot has an Active BotVersion (flat query on BotVersion,
136
+ # no correlated subquery). Agent Access on an inactive bot has no runtime effect.
137
+ ACTIVE_VERSION_COUNT=$(sf data query \
138
+ --query "SELECT COUNT() FROM BotVersion WHERE BotDefinitionId = '${BOT_DEF_ID}' AND Status = 'Active'" \
139
+ --target-org "$ORG_ALIAS" \
140
+ --json 2>/dev/null | jq -r '.result.totalSize // 0' 2>/dev/null | head -1 | tr -cd '0-9' || echo "0")
141
+ ACTIVE_VERSION_COUNT="${ACTIVE_VERSION_COUNT:-0}"
142
+
143
+ if [[ "$ACTIVE_VERSION_COUNT" -lt 1 ]]; then
144
+ log_warn "Agent BotDefinition '${BOT_DEF_NAME:-<unknown>}' has no Active BotVersion — cannot define Agent Access yet."
145
+ echo " Activate the agent version first, then re-run this script."
146
+ exit 0
147
+ fi
148
+ log_pass "Found active agent BotDefinition: ${BOT_DEF_NAME:-<unknown>} ($BOT_DEF_ID)"
149
+
150
+ # ------------------------------------------------------------
151
+ # Step 1: Create the custom permission set (idempotent)
152
+ # PermissionSet is createable via the STANDARD Data API (NOT Tooling).
153
+ # Omitting License leaves LicenseId null (= "None"), which is required so the
154
+ # permset can be added as a component of the managed PSG.
155
+ # ------------------------------------------------------------
156
+ PS_ID=$(sf data query \
157
+ --query "SELECT Id FROM PermissionSet WHERE Name = '${ACCESS_PS_NAME}'" \
158
+ --target-org "$ORG_ALIAS" \
159
+ --json 2>/dev/null | jq -r '.result.records[0].Id // empty' 2>/dev/null || echo "")
160
+
161
+ if [[ -n "$PS_ID" ]]; then
162
+ log_pass "Custom permission set '${ACCESS_PS_NAME}' already exists ($PS_ID)"
163
+ else
164
+ log_try "Creating custom permission set '${ACCESS_PS_NAME}' (License=None)..."
165
+ CREATE_PS=$(sf data create record \
166
+ --sobject PermissionSet \
167
+ --values "Name='${ACCESS_PS_NAME}' Label='${ACCESS_PS_LABEL}'" \
168
+ --target-org "$ORG_ALIAS" \
169
+ --json 2>/dev/null || echo '{"status":1}')
170
+
171
+ PS_ID=$(echo "$CREATE_PS" | jq -r '.result.id // empty' 2>/dev/null || echo "")
172
+ if [[ -z "$PS_ID" ]]; then
173
+ # Re-query in case of a race / partial output. A concurrent create or a
174
+ # not-yet-visible write can make the row lag briefly, so retry with a short
175
+ # backoff rather than giving up after a single lookup.
176
+ for _ in $(seq 1 5); do
177
+ PS_ID=$(sf data query \
178
+ --query "SELECT Id FROM PermissionSet WHERE Name = '${ACCESS_PS_NAME}'" \
179
+ --target-org "$ORG_ALIAS" \
180
+ --json 2>/dev/null | jq -r '.result.records[0].Id // empty' 2>/dev/null || echo "")
181
+ [[ -n "$PS_ID" ]] && break
182
+ sleep 2
183
+ done
184
+ fi
185
+
186
+ if [[ -z "$PS_ID" ]]; then
187
+ log_fail "Could not create permission set '${ACCESS_PS_NAME}'"
188
+ echo "$CREATE_PS" | jq -r '.message // .result.message // empty' 2>/dev/null || true
189
+ exit 1
190
+ fi
191
+ log_pass "Created custom permission set '${ACCESS_PS_NAME}' ($PS_ID)"
192
+ fi
193
+
194
+ # ------------------------------------------------------------
195
+ # Step 2: Add the agent to Agent Access on that permission set
196
+ # SetupEntityAccess: Create/Delete/Query only (NO Update). SetupEntityType is
197
+ # derived from SetupEntityId — do NOT set it on insert. BotDefinition is a valid
198
+ # SetupEntityType only at API v64.0+ (sf CLI default is well above this).
199
+ # ------------------------------------------------------------
200
+ EXISTING_ACCESS=$(sf data query \
201
+ --query "SELECT Id FROM SetupEntityAccess WHERE ParentId = '${PS_ID}' AND SetupEntityId = '${BOT_DEF_ID}'" \
202
+ --target-org "$ORG_ALIAS" \
203
+ --json 2>/dev/null | jq -r '.result.records[0].Id // empty' 2>/dev/null || echo "")
204
+
205
+ if [[ -n "$EXISTING_ACCESS" ]]; then
206
+ log_pass "Agent Access already granted to '${ACCESS_PS_NAME}' ($EXISTING_ACCESS)"
207
+ else
208
+ log_try "Granting Agent Access (SetupEntityAccess) to '${ACCESS_PS_NAME}'..."
209
+ CREATE_ACCESS=$(sf data create record \
210
+ --sobject SetupEntityAccess \
211
+ --values "ParentId='${PS_ID}' SetupEntityId='${BOT_DEF_ID}'" \
212
+ --target-org "$ORG_ALIAS" \
213
+ --json 2>/dev/null || echo '{"status":1}')
214
+
215
+ # The query-then-create above is non-atomic: a concurrent run can insert the
216
+ # same grant between our SELECT and INSERT, so tolerate DUPLICATE_VALUE — the
217
+ # grant exists either way, which is the outcome we want (idempotent).
218
+ if echo "$CREATE_ACCESS" | jq -e '.result.id // empty' >/dev/null 2>&1; then
219
+ log_pass "Agent Access granted (agent added to '${ACCESS_PS_NAME}')"
220
+ elif echo "$CREATE_ACCESS" | jq -r '.message // .result.message // ""' 2>/dev/null | grep -qi "DUPLICATE_VALUE"; then
221
+ log_pass "Agent Access already granted (duplicate insert ignored)"
222
+ else
223
+ ACCESS_ERR=$(echo "$CREATE_ACCESS" | jq -r '.message // .result.message // "unknown"' 2>/dev/null || echo "unknown")
224
+ log_fail "Could not grant Agent Access: $ACCESS_ERR"
225
+ echo " SetupEntityAccess requires a CUSTOM permission set and API v64.0+."
226
+ exit 1
227
+ fi
228
+ fi
229
+
230
+ # ------------------------------------------------------------
231
+ # Step 3: Add the custom permset as a component of the managed PSG
232
+ # PermissionSetGroupComponent is a TOOLING API object — must use --use-tooling-api.
233
+ # ------------------------------------------------------------
234
+ PSG_ID=$(sf data query \
235
+ --query "SELECT Id FROM PermissionSetGroup WHERE DeveloperName = '${USER_PSG_NAME}'" \
236
+ --target-org "$ORG_ALIAS" \
237
+ --use-tooling-api \
238
+ --json 2>/dev/null | jq -r '.result.records[0].Id // empty' 2>/dev/null || echo "")
239
+
240
+ COMPONENT_LINKED=0
241
+ if [[ -z "$PSG_ID" ]]; then
242
+ log_warn "PSG '${USER_PSG_NAME}' not found — skipping component link + recalc."
243
+ echo " The custom permission set '${ACCESS_PS_NAME}' still carries Agent Access;"
244
+ echo " it can be assigned directly to users as a fallback (see below)."
245
+ else
246
+ EXISTING_COMPONENT=$(sf data query \
247
+ --query "SELECT Id FROM PermissionSetGroupComponent WHERE PermissionSetGroupId = '${PSG_ID}' AND PermissionSetId = '${PS_ID}'" \
248
+ --target-org "$ORG_ALIAS" \
249
+ --use-tooling-api \
250
+ --json 2>/dev/null | jq -r '.result.records[0].Id // empty' 2>/dev/null || echo "")
251
+
252
+ if [[ -n "$EXISTING_COMPONENT" ]]; then
253
+ log_pass "'${ACCESS_PS_NAME}' is already a component of '${USER_PSG_NAME}'"
254
+ COMPONENT_LINKED=1
255
+ else
256
+ log_try "Adding '${ACCESS_PS_NAME}' as a component of '${USER_PSG_NAME}'..."
257
+ CREATE_COMPONENT=$(sf data create record \
258
+ --sobject PermissionSetGroupComponent \
259
+ --values "PermissionSetGroupId='${PSG_ID}' PermissionSetId='${PS_ID}'" \
260
+ --target-org "$ORG_ALIAS" \
261
+ --use-tooling-api \
262
+ --json 2>/dev/null || echo '{"status":1}')
263
+
264
+ if echo "$CREATE_COMPONENT" | jq -e '.result.id // empty' >/dev/null 2>&1; then
265
+ log_pass "Linked '${ACCESS_PS_NAME}' into '${USER_PSG_NAME}'"
266
+ COMPONENT_LINKED=1
267
+ elif echo "$CREATE_COMPONENT" | jq -r '.message // .result.message // ""' 2>/dev/null | grep -qi "DUPLICATE_VALUE"; then
268
+ log_pass "'${ACCESS_PS_NAME}' already a component of '${USER_PSG_NAME}' (duplicate insert ignored)"
269
+ COMPONENT_LINKED=1
270
+ else
271
+ COMPONENT_ERR=$(echo "$CREATE_COMPONENT" | jq -r '.message // .result.message // "unknown"' 2>/dev/null || echo "unknown")
272
+ log_warn "Could not link custom permset into managed PSG: $COMPONENT_ERR"
273
+ echo " Falling back to direct assignment of '${ACCESS_PS_NAME}' to PSG users."
274
+ fi
275
+ fi
276
+ fi
277
+
278
+ # ------------------------------------------------------------
279
+ # Step 3b: Add the custom permset as a component of the agent-user PSG too.
280
+ # The autonomous agent user runs the suggestion flow AS the agent, so it also
281
+ # needs Agent Access — wire the same custom permset into SalesManagementAgentUserPsg.
282
+ # Mirrors Step 3 (Tooling API object; idempotent query-then-skip). No direct-
283
+ # assignment fallback: the agent user is a single system user, so the component
284
+ # link is the right (and only) path.
285
+ # ------------------------------------------------------------
286
+ AGENT_COMPONENT_LINKED=0
287
+ AGENT_PSG_ID=$(sf data query \
288
+ --query "SELECT Id FROM PermissionSetGroup WHERE DeveloperName = '${AGENT_PSG_NAME}'" \
289
+ --target-org "$ORG_ALIAS" \
290
+ --use-tooling-api \
291
+ --json 2>/dev/null | jq -r '.result.records[0].Id // empty' 2>/dev/null || echo "")
292
+
293
+ if [[ -z "$AGENT_PSG_ID" ]]; then
294
+ log_warn "PSG '${AGENT_PSG_NAME}' not found — skipping agent component link + recalc."
295
+ else
296
+ EXISTING_AGENT_COMPONENT=$(sf data query \
297
+ --query "SELECT Id FROM PermissionSetGroupComponent WHERE PermissionSetGroupId = '${AGENT_PSG_ID}' AND PermissionSetId = '${PS_ID}'" \
298
+ --target-org "$ORG_ALIAS" \
299
+ --use-tooling-api \
300
+ --json 2>/dev/null | jq -r '.result.records[0].Id // empty' 2>/dev/null || echo "")
301
+
302
+ if [[ -n "$EXISTING_AGENT_COMPONENT" ]]; then
303
+ log_pass "'${ACCESS_PS_NAME}' is already a component of '${AGENT_PSG_NAME}'"
304
+ AGENT_COMPONENT_LINKED=1
305
+ else
306
+ log_try "Adding '${ACCESS_PS_NAME}' as a component of '${AGENT_PSG_NAME}'..."
307
+ CREATE_AGENT_COMPONENT=$(sf data create record \
308
+ --sobject PermissionSetGroupComponent \
309
+ --values "PermissionSetGroupId='${AGENT_PSG_ID}' PermissionSetId='${PS_ID}'" \
310
+ --target-org "$ORG_ALIAS" \
311
+ --use-tooling-api \
312
+ --json 2>/dev/null || echo '{"status":1}')
313
+
314
+ if echo "$CREATE_AGENT_COMPONENT" | jq -e '.result.id // empty' >/dev/null 2>&1; then
315
+ log_pass "Linked '${ACCESS_PS_NAME}' into '${AGENT_PSG_NAME}'"
316
+ AGENT_COMPONENT_LINKED=1
317
+ elif echo "$CREATE_AGENT_COMPONENT" | jq -r '.message // .result.message // ""' 2>/dev/null | grep -qi "DUPLICATE_VALUE"; then
318
+ log_pass "'${ACCESS_PS_NAME}' already a component of '${AGENT_PSG_NAME}' (duplicate insert ignored)"
319
+ AGENT_COMPONENT_LINKED=1
320
+ else
321
+ AGENT_COMPONENT_ERR=$(echo "$CREATE_AGENT_COMPONENT" | jq -r '.message // .result.message // "unknown"' 2>/dev/null || echo "unknown")
322
+ log_warn "Could not link custom permset into agent PSG: $AGENT_COMPONENT_ERR"
323
+ fi
324
+ fi
325
+ fi
326
+
327
+ # ------------------------------------------------------------
328
+ # Step 4: Confirm the PSG recalculates so the added component takes effect.
329
+ # The PermissionSetGroupComponent insert in Step 3 is what actually TRIGGERS the
330
+ # async recalculation — PermissionSetGroup.Status is a system-computed field
331
+ # (Updated/Updating/Outdated/Failed), NOT client-writable. There is no CLI recalc
332
+ # verb, so we simply POLL Status until it returns to 'Updated'. A Failed status
333
+ # means Agent Access is NOT live and must be surfaced as a hard error.
334
+ # ------------------------------------------------------------
335
+ # The PermissionSetGroupComponent insert in Step 3 IS the recalc trigger — the
336
+ # async recalculation is already under way and the org converges on its own; we
337
+ # deliberately do NOT block waiting for it (recalc completion only matters when a
338
+ # user later launches the agent). We read Status once, for reporting.
339
+ if [[ "$COMPONENT_LINKED" -eq 1 && -n "$PSG_ID" ]]; then
340
+ log_try "Recalculation triggered for '${USER_PSG_NAME}' (component linked)."
341
+ PSG_STATUS=$(psg_status "$ORG_ALIAS" "$USER_PSG_NAME")
342
+ case "$PSG_STATUS" in
343
+ Updated)
344
+ log_pass "PSG '${USER_PSG_NAME}' Status: Updated" ;;
345
+ Failed)
346
+ log_fail "PSG '${USER_PSG_NAME}' recalculation Status: Failed — Agent Access not active"
347
+ exit 1 ;;
348
+ *)
349
+ log_info "PSG '${USER_PSG_NAME}' Status: ${PSG_STATUS} — recalculation is async and will complete on its own."
350
+ echo " Agent Access takes effect once Status reaches 'Updated' (verify-all.sh reports it)." ;;
351
+ esac
352
+ fi
353
+
354
+ # ------------------------------------------------------------
355
+ # Step 4b: Report the agent-user PSG recalc, mirroring Step 4 (read Status once,
356
+ # never sleep-poll — the Step 3b component insert already triggered the async
357
+ # recalculation).
358
+ # ------------------------------------------------------------
359
+ if [[ "$AGENT_COMPONENT_LINKED" -eq 1 && -n "$AGENT_PSG_ID" ]]; then
360
+ log_try "Recalculation triggered for '${AGENT_PSG_NAME}' (component linked)."
361
+ AGENT_PSG_STATUS=$(psg_status "$ORG_ALIAS" "$AGENT_PSG_NAME")
362
+ case "$AGENT_PSG_STATUS" in
363
+ Updated)
364
+ log_pass "PSG '${AGENT_PSG_NAME}' Status: Updated" ;;
365
+ Failed)
366
+ log_fail "PSG '${AGENT_PSG_NAME}' recalculation Status: Failed — Agent Access not active"
367
+ exit 1 ;;
368
+ *)
369
+ log_info "PSG '${AGENT_PSG_NAME}' Status: ${AGENT_PSG_STATUS} — recalculation is async and will complete on its own."
370
+ echo " Agent Access takes effect once Status reaches 'Updated' (verify-all.sh reports it)." ;;
371
+ esac
372
+ fi
373
+
374
+ # ------------------------------------------------------------
375
+ # Fallback: if the component link failed, assign the custom permset directly to
376
+ # the users who hold the managed PSG so they still get Agent Access.
377
+ # ------------------------------------------------------------
378
+ if [[ "$COMPONENT_LINKED" -eq 0 && -n "$PSG_ID" ]]; then
379
+ log_info "Fallback: assigning '${ACCESS_PS_NAME}' directly to users of '${USER_PSG_NAME}'..."
380
+ # LIMIT 200 caps the fan-out: each user below costs one sequential API call, so
381
+ # an unbounded query on a large org could issue hundreds of calls. The linked
382
+ # component (Step 3) is the scalable path; this direct-assignment fallback is a
383
+ # best-effort stopgap. If the PSG has >200 users, prefer fixing the component
384
+ # link so recalculation reaches everyone at once.
385
+ ASSIGNEE_IDS=$(sf data query \
386
+ --query "SELECT AssigneeId FROM PermissionSetAssignment WHERE PermissionSetGroup.DeveloperName = '${USER_PSG_NAME}' LIMIT 200" \
387
+ --target-org "$ORG_ALIAS" \
388
+ --json 2>/dev/null | jq -r '.result.records[].AssigneeId' 2>/dev/null || echo "")
389
+
390
+ if [[ -z "$ASSIGNEE_IDS" ]]; then
391
+ log_warn "No users currently hold '${USER_PSG_NAME}' — nothing to assign."
392
+ else
393
+ # NOTE: do not name this loop var UID — UID is a readonly special var in bash.
394
+ while IFS= read -r ASSIGNEE_ID; do
395
+ [[ -z "$ASSIGNEE_ID" ]] && continue
396
+ CREATE_RESULT=$(sf data create record \
397
+ --sobject PermissionSetAssignment \
398
+ --values "AssigneeId='${ASSIGNEE_ID}' PermissionSetId='${PS_ID}'" \
399
+ --target-org "$ORG_ALIAS" \
400
+ --json 2>/dev/null || echo '{"status":1}')
401
+
402
+ # Distinguish "already assigned" (benign) from real failures like a
403
+ # deactivated user or license conflict — don't paper over the latter.
404
+ RESULT_MSG=$(echo "$CREATE_RESULT" | jq -r '.message // .result.message // ""' 2>/dev/null || echo "")
405
+ if echo "$CREATE_RESULT" | jq -e '.result.id // empty' >/dev/null 2>&1; then
406
+ log_pass "Assigned '${ACCESS_PS_NAME}' to user $ASSIGNEE_ID"
407
+ elif echo "$RESULT_MSG" | grep -qi "DUPLICATE_VALUE"; then
408
+ log_info "Already assigned: $ASSIGNEE_ID"
409
+ elif echo "$RESULT_MSG" | grep -qi "FIELD_INTEGRITY_EXCEPTION"; then
410
+ log_warn "License conflict for $ASSIGNEE_ID (FIELD_INTEGRITY_EXCEPTION) — user may need a license upgrade"
411
+ elif echo "$RESULT_MSG" | grep -qi "INVALID_CROSS_REFERENCE_KEY"; then
412
+ log_warn "Assignment for $ASSIGNEE_ID rejected (INVALID_CROSS_REFERENCE_KEY) — the PSG has not finished recalculating (Status not yet 'Updated'). Re-run after recalculation completes."
413
+ else
414
+ ASSIGN_ERR="${RESULT_MSG:-unknown}"
415
+ log_warn "Failed to assign to $ASSIGNEE_ID: $ASSIGN_ERR"
416
+ fi
417
+ done <<< "$ASSIGNEE_IDS"
418
+ fi
419
+ fi
420
+
421
+ echo ""
422
+ echo "=== Agent Access definition complete ==="
423
+ echo ""
424
+ echo " Custom permission set : ${ACCESS_PS_NAME} ($PS_ID)"
425
+ echo " Agent : ${BOT_DEF_NAME:-<unknown>} ($BOT_DEF_ID)"
426
+ echo " Target PSG : ${USER_PSG_NAME}"
427
+ echo " Agent PSG : ${AGENT_PSG_NAME}"
428
+ echo ""
429
+ echo " Users holding '${USER_PSG_NAME}' can launch the Sales Agent once the PSG"
430
+ echo " recalculation completes (Status 'Updated'). The recalc runs asynchronously."
@@ -0,0 +1,77 @@
1
+ #!/bin/bash
2
+
3
+ # Deploy Pipeline Management settings from local metadata files
4
+ # Usage: ./deploy-settings.sh <org-alias>
5
+
6
+ set -euo pipefail
7
+
8
+ # sf CLI can emit ANSI color codes inside --json stdout, breaking jq parsing.
9
+ # 2>/dev/null only strips stderr; these env vars are the reliable fix (see PM notes).
10
+ export NO_COLOR=1
11
+ export FORCE_COLOR=0
12
+
13
+ ORG_ALIAS="${1:-}"
14
+
15
+ if [[ -z "$ORG_ALIAS" ]]; then
16
+ echo "❌ Error: Missing org alias"
17
+ echo "Usage: $0 <org-alias>"
18
+ exit 1
19
+ fi
20
+
21
+ if [[ ! -f "sfdx-project.json" ]]; then
22
+ echo "❌ Error: sfdx-project.json not found in current directory"
23
+ echo "Copy from ../assets/sfdx-project.json first"
24
+ exit 1
25
+ fi
26
+
27
+ SETTINGS_DIR="force-app/main/default/settings"
28
+
29
+ if [[ ! -d "$SETTINGS_DIR" ]]; then
30
+ echo "❌ Error: Settings directory not found: $SETTINGS_DIR"
31
+ echo "Run ./retrieve-settings.sh first to retrieve settings"
32
+ exit 1
33
+ fi
34
+
35
+ echo "📤 Deploying Pipeline Management settings to org: $ORG_ALIAS"
36
+ echo ""
37
+
38
+ # Count available settings files
39
+ SETTINGS_COUNT=$(find "$SETTINGS_DIR" -name "*.settings-meta.xml" -type f | wc -l | tr -d ' ')
40
+
41
+ if [[ "$SETTINGS_COUNT" -eq 0 ]]; then
42
+ echo "❌ No settings files found in $SETTINGS_DIR"
43
+ exit 1
44
+ fi
45
+
46
+ echo "Found $SETTINGS_COUNT settings file(s) to deploy"
47
+ echo ""
48
+
49
+ # Deploy all settings
50
+ echo "Deploying settings..."
51
+
52
+ if sf project deploy start \
53
+ --source-dir "$SETTINGS_DIR" \
54
+ --target-org "$ORG_ALIAS" \
55
+ --json 2>/dev/null | jq -e '.status == 0' >/dev/null 2>&1; then
56
+ echo "✅ Settings deployed successfully"
57
+ else
58
+ echo "❌ Deployment failed"
59
+ echo ""
60
+ echo "Retrying with verbose output..."
61
+ sf project deploy start \
62
+ --source-dir "$SETTINGS_DIR" \
63
+ --target-org "$ORG_ALIAS"
64
+ exit 1
65
+ fi
66
+
67
+ echo ""
68
+
69
+ # List deployed files
70
+ echo "Deployed settings:"
71
+ find "$SETTINGS_DIR" -name "*.settings-meta.xml" -type f | while read -r FILE; do
72
+ BASENAME=$(basename "$FILE" .settings-meta.xml)
73
+ echo " ✅ $BASENAME"
74
+ done
75
+
76
+ echo ""
77
+ echo "✅ Deployment complete"