@salesforce/afv-skills 1.31.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.
- package/package.json +1 -1
- package/skills/agentforce-bot-upgrade/SKILL.md +217 -0
- package/skills/agentforce-bot-upgrade/references/extraction-blueprint.md +242 -0
- package/skills/agentforce-bot-upgrade/references/generate-agent-spec.md +128 -0
- package/skills/agentforce-bot-upgrade/references/handoff-output-format.md +208 -0
- package/skills/agentforce-bot-upgrade/references/input-contract.md +101 -0
- package/skills/agentforce-bot-upgrade/references/mapping-rules.md +113 -0
- package/skills/agentforce-bot-upgrade/references/planner-workflow-reference.md +78 -0
- package/skills/agentforce-bot-upgrade/references/post-conversion-enhancements-reference.md +67 -0
- package/skills/agentforce-bot-upgrade/references/quality-checklist.md +33 -0
- package/skills/agentforce-bot-upgrade/references/sf-cli-bot-reference.md +116 -0
- package/skills/dx-devops-work-item-manage/SKILL.md +270 -0
- package/skills/dx-devops-work-item-manage/examples/common-workflows.md +479 -0
- package/skills/dx-devops-work-item-manage/references/cli-commands.md +476 -0
- package/skills/dx-org-manage/SKILL.md +8 -5
- package/skills/dx-org-trial-expiration-check/SKILL.md +239 -0
- package/skills/dx-org-trial-expiration-check/scripts/check_expiration.sh +588 -0
- package/skills/experience-ui-bundle-app-coordinate/SKILL.md +12 -9
- package/skills/experience-ui-bundle-custom-app-generate/SKILL.md +29 -5
- package/skills/experience-ui-bundle-custom-app-generate/references/configure-metadata-custom-application.md +1 -1
- package/skills/experience-ui-bundle-custom-app-generate/scripts/resolve-uibundle-path.sh +30 -0
- package/skills/experience-ui-bundle-features-generate/SKILL.md +86 -11
- package/skills/experience-ui-bundle-features-generate/references/conflict-resolution-schema.json +20 -0
- package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +14 -0
- package/skills/experience-ui-bundle-frontend-generate/SKILL.md +6 -1
- package/skills/experience-ui-bundle-frontend-generate/references/component.md +34 -1
- package/skills/experience-ui-bundle-frontend-generate/references/page.md +1 -1
- package/skills/experience-ui-bundle-project-generate/SKILL.md +111 -0
- package/skills/experience-ui-bundle-project-generate/scripts/flatten-project.mjs +33 -0
- package/skills/platform-custom-lightning-type-generate/SKILL.md +9 -14
- package/skills/platform-custom-lightning-type-generate/references/widget-rendition.md +27 -111
- package/skills/platform-lightning-type-widget-coordinate/SKILL.md +214 -0
- package/skills/platform-lightning-type-widget-coordinate/examples/existing-lightning-type-with-widget-prompt.md +80 -0
- package/skills/platform-lightning-type-widget-coordinate/examples/new-lightning-type-with-widget-prompt.md +75 -0
- package/skills/platform-lightning-type-widget-coordinate/references/build-plan-format.md +67 -0
- package/skills/platform-lightning-type-widget-coordinate/references/lightning-type-discovery.md +124 -0
- package/skills/platform-lightning-type-widget-coordinate/references/validation-gates.md +130 -0
- package/skills/platform-sharing-owd-configure/SKILL.md +142 -0
- package/skills/platform-sharing-owd-configure/examples/get_owd_output.md +38 -0
- package/skills/platform-sharing-owd-configure/examples/update_owd_output.md +44 -0
- package/skills/platform-sharing-owd-configure/references/access_levels.md +49 -0
- package/skills/platform-sharing-owd-configure/references/metadata_api_approach.md +69 -0
- package/skills/platform-sharing-rules-generate/SKILL.md +89 -19
- package/skills/platform-sharing-rules-generate/examples/test-cases.md +381 -0
- package/skills/platform-widget-generate/SKILL.md +241 -0
- package/skills/platform-widget-generate/examples/conditional.json +73 -0
- package/skills/platform-widget-generate/examples/list-with-foreach.json +66 -0
- package/skills/platform-widget-generate/examples/single-object.json +64 -0
- package/skills/platform-widget-generate/references/schema-from-lightning-type.md +76 -0
- package/skills/platform-widget-generate/references/widget-bundle-layout.md +116 -0
- package/skills/platform-widget-generate/references/widget-meta-directives.md +130 -0
- package/skills/sales-agentforce-pipeline-management-configure/SKILL.md +268 -0
- package/skills/sales-agentforce-pipeline-management-configure/assets/field-completion-template.genAiPromptTemplate-meta.xml +141 -0
- package/skills/sales-agentforce-pipeline-management-configure/assets/pipeline_management_flow.flow-meta.xml +548 -0
- package/skills/sales-agentforce-pipeline-management-configure/assets/sales_management_agent.agent +2938 -0
- package/skills/sales-agentforce-pipeline-management-configure/assets/sales_management_agent.bundle-meta.xml +4 -0
- package/skills/sales-agentforce-pipeline-management-configure/assets/sfdx-project.json +12 -0
- package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/bant.txt +11 -0
- package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/meddic.txt +11 -0
- package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/spiced.txt +11 -0
- package/skills/sales-agentforce-pipeline-management-configure/examples/custom-prompt-instructions.md +317 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/admin-communication.md +355 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/agent-creation.md +383 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/auth-and-cli.md +146 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/automation-matrix.md +23 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/autonomous-updates.md +215 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/canonical-agent-path.md +75 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/data-sources.md +155 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/field-completion-prompt-template.md +120 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/field-completion-prompts.md +149 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/flags.md +13 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/flow-clone-from-template.md +447 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/metadata-inventory.md +330 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/opportunity-stages.md +246 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/repair-diagnostics.md +265 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/setup-order.md +263 -0
- package/skills/sales-agentforce-pipeline-management-configure/references/soap-api-enablement.md +528 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/CHANGES.md +180 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/README.md +56 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/add-field-suggestion.sh +679 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/create-agent.sh +302 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/create-flow.sh +250 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/define-agent-access.sh +430 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/deploy-settings.sh +77 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/enable-deal-agent.sh +241 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/enable-prerequisites.sh +538 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/flow-debug-and-verify.sh +385 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/retrieve-settings.sh +131 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/setup-all.sh +2791 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/agent-bundle-publish.sh +189 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/agent-detection.sh +239 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/flow-builder.sh +238 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/psg.sh +48 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/soap.sh +140 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/stage-descriptions.sh +448 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/test-opp.sh +166 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/verify-all.sh +636 -0
- package/skills/sales-agentforce-pipeline-management-configure/scripts/verify-prompt-generation.sh +346 -0
package/skills/sales-agentforce-pipeline-management-configure/assets/sales_management_agent.agent
ADDED
|
@@ -0,0 +1,2938 @@
|
|
|
1
|
+
# Sales Management Agent (Pipeline Management)
|
|
2
|
+
# ==================================================
|
|
3
|
+
#
|
|
4
|
+
# The Sales Management Agent generates opportunity field update suggestions
|
|
5
|
+
# (Stage, Next Step, custom text fields) based on notes, emails, and
|
|
6
|
+
# conversation insights. It runs autonomously on a daily schedule.
|
|
7
|
+
#
|
|
8
|
+
# Deployment:
|
|
9
|
+
# sf agent publish authoring-bundle --json --api-name SalesAgent --target-org <alias>
|
|
10
|
+
#
|
|
11
|
+
# Prerequisites:
|
|
12
|
+
# - Einstein Generative AI enabled (EinsteinGptSettings.enableEinsteinGptPlatform)
|
|
13
|
+
# - Agentforce Agent enabled (EinsteinCopilotSettings.enableEinsteinGptCopilot)
|
|
14
|
+
# - Enhanced Notes enabled (EnhancedNotesSettings.enableEnhancedNotes)
|
|
15
|
+
# - Pipeline Management enabled (SalesDealAgentSettings.enableDealAgent)
|
|
16
|
+
# - Schedule-triggered flow active (Process_Field_Update_Suggestions)
|
|
17
|
+
# - OpptStageDescription records populated for all active stages
|
|
18
|
+
#
|
|
19
|
+
# Architecture:
|
|
20
|
+
# Schedule Trigger (Daily) -> Get Eligible Opportunities -> Loop:
|
|
21
|
+
# -> getOrExecFieldUpdtSuggestion: RecommendStageforOpp
|
|
22
|
+
# -> getOrExecFieldUpdtSuggestion: RecommendNextStepforOpp
|
|
23
|
+
#
|
|
24
|
+
# Note: This is the genuine NGA Sales Agent definition (developer_name:
|
|
25
|
+
# "SalesAgent", agent_template: "SalesMgmt__NGASalesAgent"). The agent is
|
|
26
|
+
# created by PUBLISHING this bundle (sf agent publish authoring-bundle) — it is
|
|
27
|
+
# NOT auto-provisioned when Pipeline Management is enabled. Enabling Pipeline
|
|
28
|
+
# Management provisions the agent USER and permission set groups only. See
|
|
29
|
+
# references/agent-creation.md.
|
|
30
|
+
|
|
31
|
+
system:
|
|
32
|
+
instructions: "You're an AI Sales agent that helps sales reps throughout the sales process."
|
|
33
|
+
messages:
|
|
34
|
+
welcome: "Hi there! I'm your Sales Agent. I help you through every step of the sales process by automating time-consuming tasks like record updates and providing timely, data-informed suggestions. What would you like to work on today?"
|
|
35
|
+
error: "Sorry, it looks like something has gone wrong. Let us start over."
|
|
36
|
+
|
|
37
|
+
config:
|
|
38
|
+
agent_label: "Sales Agent"
|
|
39
|
+
agent_template: "SalesMgmt__NGASalesAgent"
|
|
40
|
+
agent_type: "AgentforceEmployeeAgent"
|
|
41
|
+
developer_name: "SalesAgent"
|
|
42
|
+
description: "Help sales reps through every step of the sales process. Customize your agent with skills to fit your sales team's needs."
|
|
43
|
+
company: "Your company specializes in providing essential products and strategic solutions designed to enhance your customer's business performance. Its core offerings help companies improve efficiency and achieve their strategic objectives."
|
|
44
|
+
enable_enhanced_event_logs: False
|
|
45
|
+
additional_parameter__reset_to_initial_node: False
|
|
46
|
+
additional_parameter__global_parallel_tool_calls_enabled: True
|
|
47
|
+
additional_parameter__enable_serial_tool_state_chaining: "Research_Specialist,Pipeline_Management_Specialist,CRM_Record_Management_Utility"
|
|
48
|
+
|
|
49
|
+
language:
|
|
50
|
+
default_locale: "en_US"
|
|
51
|
+
additional_locales: "en_US"
|
|
52
|
+
all_additional_locales: False
|
|
53
|
+
|
|
54
|
+
variables:
|
|
55
|
+
currentIntentCode: mutable string = ""
|
|
56
|
+
visibility: "Internal"
|
|
57
|
+
description: "Tracks the current intent code for the utterance (e.g., pov_meeting_prep, conversation_insights). Cleared after utterance processing completes."
|
|
58
|
+
currentObjectApiName: mutable string = ""
|
|
59
|
+
visibility: "External"
|
|
60
|
+
description: "The API name of the Salesforce object (such as Account or Opportunity) associated with the record the user wants to interact with. Do not use this if the user is already talking about another object in the conversation."
|
|
61
|
+
currentRecordId: mutable string = ""
|
|
62
|
+
visibility: "External"
|
|
63
|
+
description: "The ID of the record on the user's screen. It may not relate to the user's input. Only use this if the user input mentions 'this', 'current', 'the record', etc. If in doubt, don't use it."
|
|
64
|
+
mainRecordId: mutable string = ""
|
|
65
|
+
visibility: "Internal"
|
|
66
|
+
description: "The ID of the record that the agent is working on now, could come from the page context or user's utterance."
|
|
67
|
+
currentRecordName: mutable string = ""
|
|
68
|
+
visibility: "Internal"
|
|
69
|
+
description: "The record name extracted from the user's utterance."
|
|
70
|
+
currentAccountName: mutable string = ""
|
|
71
|
+
visibility: "Internal"
|
|
72
|
+
description: "The account name either same as currentRecordName or derived from the parent account."
|
|
73
|
+
currentAccountId: mutable string = ""
|
|
74
|
+
visibility: "Internal"
|
|
75
|
+
description: "The account id either same as mainRecordId or derived from the parent account."
|
|
76
|
+
transcriptCallId: mutable string = ""
|
|
77
|
+
visibility: "Internal"
|
|
78
|
+
description: "The 0LQ/6qr Voice or Video Call Id, set via Set_TranscriptCallId only AFTER a valid Get_Conversation_Intelligence call, that gates and supplies the recordId for Get_Conversation_Transcript. Empty means the transcript action is not yet callable."
|
|
79
|
+
currentObjectType: mutable string = ""
|
|
80
|
+
visibility: "Internal"
|
|
81
|
+
description: "The Salesforce object API name extracted from the user's utterance (e.g., 'Account', 'Opportunity')."
|
|
82
|
+
today: mutable date = None
|
|
83
|
+
visibility: "Internal"
|
|
84
|
+
description: "today's date in YYYY-MM-DD format"
|
|
85
|
+
thirtyDaysAgo: mutable date = None
|
|
86
|
+
visibility: "Internal"
|
|
87
|
+
description: "The date in YYYY-MM-DD format 30 days before today"
|
|
88
|
+
crmDataFetched: mutable string = ""
|
|
89
|
+
visibility: "Internal"
|
|
90
|
+
description: "Gate main data action calling with value as @variables.mainRecordId + ':' + @variables.currentIntentCode"
|
|
91
|
+
getRecordDetailsResult: mutable string = ""
|
|
92
|
+
visibility: "Internal"
|
|
93
|
+
description: "Saved getRecordDetails output from before reasoning call"
|
|
94
|
+
getActivitiesTimelineResult: mutable string = ""
|
|
95
|
+
visibility: "Internal"
|
|
96
|
+
description: "Saved getActivitiesTimeline output from before reasoning call"
|
|
97
|
+
getRelatedEmailResult: mutable list[object] = ""
|
|
98
|
+
visibility: "Internal"
|
|
99
|
+
description: "Saved getRelatedEmail output from before reasoning call"
|
|
100
|
+
getRelatedNotesResult: mutable list[object] = ""
|
|
101
|
+
visibility: "Internal"
|
|
102
|
+
description: "Saved getRelatedNotes output from before reasoning call"
|
|
103
|
+
slack_channel_ids: mutable string = ""
|
|
104
|
+
description: "Comma-separated list of Slack channel IDs to share a canvas with."
|
|
105
|
+
slack_user_ids: mutable string = ""
|
|
106
|
+
description: "Comma-separated list of Slack user IDs to share a canvas with."
|
|
107
|
+
access_level: mutable string = "view"
|
|
108
|
+
description: "Access level to grant when sharing a Slack canvas (e.g., view, edit)."
|
|
109
|
+
|
|
110
|
+
knowledge:
|
|
111
|
+
rag_feature_config_id: ""
|
|
112
|
+
citations_enabled: False
|
|
113
|
+
citations_url: ""
|
|
114
|
+
|
|
115
|
+
start_agent Sales_Management_Router:
|
|
116
|
+
description: "Hub node that routes user requests to specialized subagent nodes based on record type and intent"
|
|
117
|
+
reasoning:
|
|
118
|
+
instructions: ->
|
|
119
|
+
|
|
|
120
|
+
# Record Name & Object Type Extraction
|
|
121
|
+
Parse ONLY the user's MOST RECENT message — ignore ALL prior conversation history when extracting name and object type. The most recent message is the ONLY source of truth for what record and intent this request is about.
|
|
122
|
+
- Examples:
|
|
123
|
+
- "account acme" → recordName="acme", objectType="Account"
|
|
124
|
+
- "opportunity meta" → recordName="meta", objectType="Opportunity"
|
|
125
|
+
- "contact john smith" → recordName="john smith", objectType="Contact"
|
|
126
|
+
- "lead jane doe" → recordName="jane doe", objectType="Lead"
|
|
127
|
+
- "POV for Meta" → recordName="Meta", objectType="" (none mentioned)
|
|
128
|
+
- "Meeting prep for Robert Garcia" → recordName="Robert Garcia", objectType="" (NO type keyword — leave empty; do NOT guess "Contact" from a person-like name)
|
|
129
|
+
There are cases where one or neither is mentioned — only extract what is present.
|
|
130
|
+
|
|
131
|
+
# Object Type Rule (HARD — do NOT guess for person names)
|
|
132
|
+
Set objectType from an EXPLICIT keyword ("account", "customer", "opportunity"/"opp"/"oppty", "contact", "lead", "event") or from an id prefix (001/006/003/00Q/00U).
|
|
133
|
+
SPECIAL CASE — person-like names (First Last pattern, e.g. "Robert Garcia", "John Gardner", "Jane Doe"): you MUST leave objectType="" (empty). A person's name is NOT evidence of "Contact" — it could equally be a Lead. Leaving objectType empty lets the SOSL search cover all object types and avoids missing Leads.
|
|
134
|
+
Company/org names (e.g. "American Express", "Salesforce", "Acme Corp", "Meta") where no explicit type keyword is present: infer objectType="Account". Company-looking names are almost always Accounts; scoping the search improves SOSL hit rate.
|
|
135
|
+
When in doubt (name is ambiguous — could be a person or a company): leave objectType empty.
|
|
136
|
+
|
|
137
|
+
Mapping to Salesforce API names:
|
|
138
|
+
- "account", "customer", id starting with "001" → "Account"
|
|
139
|
+
- "opportunity", "opp", "oppty" or id starting with "006" → "Opportunity"
|
|
140
|
+
- "contact" or id starting with "003" → "Contact"
|
|
141
|
+
- "lead" or id starting with "00Q" → "Lead"
|
|
142
|
+
- "event" or id starting with "00U" → "Event"
|
|
143
|
+
|
|
144
|
+
# Intent Extraction with an Intent Codes
|
|
145
|
+
Map user requests to intent codes:
|
|
146
|
+
## Account Ones
|
|
147
|
+
- "POV" OR "Meeting prep" OR "Briefing" OR "Brief" OR "Strategy" (when NOT scoped to a specific Opportunity/Deal) → intent code: `pov_meeting_prep`
|
|
148
|
+
- "Conversation Insights" OR "Call history" OR "What did we talk about" OR "Call history" OR "Recent syncs" OR "Meeting notes" OR "Call recap" OR "Interaction summary" → intent code: `conversation_insights`
|
|
149
|
+
- "Account Plan" OR "Plan status" OR "Show plan" OR "Account Plan" OR "Plan status" OR "What is the strategy?" OR "What are our goals?" OR "Plan summary" → intent code: `account_plan`
|
|
150
|
+
- Web research only related utterance like "Company news" OR "Company updates" OR "what are the new CRM industry trends" OR "what is the recent news about Tesla" OR "External profile" OR "Who are they?" → intent code: `web_research`
|
|
151
|
+
- "Snapshot" OR "Account overview" OR "Account details" OR "CRM view" OR "Record data" OR "Quick look" OR "Key fields" → intent code: `crm_snapshot`
|
|
152
|
+
|
|
153
|
+
## PIPELINE FIELD REQUESTS — assign intentCode = "field_update_suggestion" (evaluate THIS FIRST, before Deal Analysis, before all other intents — NEVER assign a research code)
|
|
154
|
+
HARD RULE: If the message contains ANY of the following phrases, assign intentCode = "field_update_suggestion" IMMEDIATELY. Do NOT evaluate Deal Analysis. Do NOT check for an Opportunity name. The presence of a deal name does NOT change this.
|
|
155
|
+
Trigger phrases (any one is sufficient):
|
|
156
|
+
- "suggest next step", "suggest the next step"
|
|
157
|
+
- "suggest stage", "suggest the stage"
|
|
158
|
+
- "recommend next step", "recommend the next step"
|
|
159
|
+
- "recommend stage", "recommend the stage"
|
|
160
|
+
- "update next step", "update stage"
|
|
161
|
+
- "what should the next step be", "what should the stage be"
|
|
162
|
+
Examples:
|
|
163
|
+
- "Suggest next step for UPS - Drive digital transformation initiatives" → field_update_suggestion (Pipeline)
|
|
164
|
+
- "Suggest stage for American Express" → field_update_suggestion (Pipeline)
|
|
165
|
+
- "Recommend next step for this deal" → field_update_suggestion (Pipeline)
|
|
166
|
+
These are Pipeline field update requests. The PRE-CHECK routes them directly to Pipeline.
|
|
167
|
+
|
|
168
|
+
## Deal Analysis Intent Codes (HARD — match AFTER the PIPELINE FIELD REQUESTS check above, BEFORE the general intents below)
|
|
169
|
+
Any request scoped to a specific Opportunity/Deal (recordId starts with "006", or user says "deal", "opportunity", or names a known deal) MUST map to one of the four deal-analysis intents below.
|
|
170
|
+
- "Generate a deal brief for [Deal]" OR "deal summary" OR "summarize the deal" OR "overview of the deal" → intent code: `deal_summary`
|
|
171
|
+
- "Summarize the most recent interaction for [Deal]" OR "what just happened on [Deal]" OR "last interaction on [Deal]" → intent code: `last_interaction`
|
|
172
|
+
- "Identify potential challenges or blockers for [Deal]" OR "deal risks" OR "what could derail [Deal]" → intent code: `deal_risk`
|
|
173
|
+
- "Recommend next best actions for [Deal]" OR "what should I do next on [Deal]" OR "how do I advance [Deal]" → intent code: `next_steps`
|
|
174
|
+
|
|
175
|
+
Canonical Trigger Utterances (HARD — these patterns MUST map to deal-analysis intents even when the user does NOT include the word "opportunity" or "deal"):
|
|
176
|
+
- "Generate a deal brief for [Name]" → `deal_summary`
|
|
177
|
+
- "Summarize the most recent interaction for [Name]" → `last_interaction`
|
|
178
|
+
- "Identify potential challenges or blockers for [Name]" → `deal_risk`
|
|
179
|
+
- "Recommend next best actions for [a deal]" OR "what should I do next on the deal" → `next_steps`
|
|
180
|
+
|
|
181
|
+
After extraction, call {!@actions.Set_Intent_Record_Name_Object_Type} with extracted record name, object type, account name (if object type is account, same as record name, otherwise empty) and intent or "" if not extracted.
|
|
182
|
+
SALESFORCE ID PASSTHROUGH: If the extracted recordName is itself a Salesforce record ID (15–18 alphanumeric characters starting with "001", "006", "003", "00Q", or "00U"), pass that same value as BOTH mainRecordId AND currentRecordName to Set_Intent_Record_Name_Object_Type. Also ensure currentIntentCode is set to the extracted intent — do NOT leave it empty. Otherwise pass mainRecordId = "" (empty string) as usual.
|
|
183
|
+
EXCEPTION A: If the message matches Rule 0 (contains "Opportunity with Id:" AND "AiGenActionItem with Id:"), skip this extraction entirely and do NOT call Set_Intent_Record_Name_Object_Type. Go directly to Rule 0.
|
|
184
|
+
EXCEPTION B: If the most recent message is a disambiguation pick (a bare number like "1", "2", or a short phrase like "the first one", "option 2") AND {!@variables.currentRecordName} is already set — the user is picking from a list for the SAME record they already asked about. Keep {!@variables.currentRecordName} and {!@variables.currentIntentCode} unchanged. Do NOT call Set_Intent_Record_Name_Object_Type. Route directly to Research Specialist (Rule 3).
|
|
185
|
+
|
|
186
|
+
Always call {!@actions.Set_Dates}.
|
|
187
|
+
|
|
188
|
+
# Routing Rules (evaluate in order)
|
|
189
|
+
|
|
190
|
+
## PRE-CHECK (evaluate AFTER Set_Intent_Record_Name_Object_Type has been called this turn)
|
|
191
|
+
This PRE-CHECK only applies when you have JUST called Set_Intent_Record_Name_Object_Type in this same reasoning turn.
|
|
192
|
+
Read the intentCode you JUST extracted and passed to Set_Intent_Record_Name_Object_Type:
|
|
193
|
+
- If it is "pov_meeting_prep", "conversation_insights", "account_plan", "web_research", "crm_snapshot", "deal_summary", "last_interaction", "deal_risk", or "next_steps" → call {!@actions.go_to_Research_Specialist} immediately. Do NOT evaluate Rule 0, 1, 2, or 2.5. STOP.
|
|
194
|
+
- If it is "field_update_suggestion" → call {!@actions.go_to_Pipeline_Management_Specialist} immediately. Do NOT evaluate any other rule. STOP.
|
|
195
|
+
- If it is "" (empty string) → fall through and evaluate the rules below based on the current user message.
|
|
196
|
+
If you have NOT just called Set_Intent_Record_Name_Object_Type this turn (e.g. you were redirected here by Return_to_Hub from another agent): call Set_Intent_Record_Name_Object_Type first, then apply this PRE-CHECK.
|
|
197
|
+
|
|
198
|
+
Rule 0 — Slack Button Response (check FIRST, before ALL other rules): If the message contains BOTH "Opportunity with Id:" AND "AiGenActionItem with Id:" → this is a Slack accept/reject button click. Call {!@actions.go_to_Pipeline_Management_Specialist} immediately. Do NOT extract record name or intent. Do NOT apply any other rule. STOP.
|
|
199
|
+
- "I accept the suggested update for the [field] field for the Opportunity with Id: [006...] and AiGenActionItem with Id: [1Rm...]" → Pipeline
|
|
200
|
+
- "I do not accept the suggested update for the [field] field for the Opportunity with Id: [006...] and AiGenActionItem with Id: [1Rm...]" → Pipeline
|
|
201
|
+
|
|
202
|
+
Rule 1 — Pipeline: ONLY if the user explicitly asks to suggest, recommend, or update the "Next Step" field or the "Stage" field on an Opportunity → call {!@actions.go_to_Pipeline_Management_Specialist}. STOP.
|
|
203
|
+
- "suggest next step", "recommend stage", "update Next Step field", "what should the Next Step field be" → Pipeline
|
|
204
|
+
- "next best actions", "recommend next steps for [Deal]", "how do I advance the deal", "what should I do next on this deal" → NOT Pipeline. These are strategic recommendations and route to Research (Rule 3 / Deal Analysis).
|
|
205
|
+
|
|
206
|
+
Rule 2 — Slack Operations: If the request is about Slack channel operations, searches, or summaries → call {!@actions.go_to_Slack_Operations}. STOP. This includes:
|
|
207
|
+
- Find Slack channel for a record
|
|
208
|
+
- Send message to Slack channel (not DM to specific user)
|
|
209
|
+
- Search Slack content
|
|
210
|
+
- Summarize Slack channel
|
|
211
|
+
- List channels or search results
|
|
212
|
+
|
|
213
|
+
Rule 2.5 — Customer Communication: If the request is about drafting, writing, or sending an email, or scheduling/sending a meeting invite → call {!@actions.go_to_Customer_Communication}. STOP. This includes:
|
|
214
|
+
- "draft an email", "write an email", "send an email", "email [contact]", "compose an email"
|
|
215
|
+
- "schedule a meeting", "set up a meeting", "book a follow-up", "send a meeting invite", "send meeting request"
|
|
216
|
+
- "follow-up email", "intro email", "outreach email", "introductory email"
|
|
217
|
+
|
|
218
|
+
Rule 3 — Research (DEFAULT): For ANY other request involving a record, entity, information need, OR Slack direct messages/notifications to specific users → call @actions.go_to_Research_Specialist. STOP. This includes:
|
|
219
|
+
- POV, meeting prep, brief, briefing, strategy
|
|
220
|
+
- Account research, recent news, company updates, web search
|
|
221
|
+
- Conversation insights, call history, interaction summary
|
|
222
|
+
- Account plan, account details, CRM snapshot, account overview
|
|
223
|
+
- Emails, notes, record details, any record-related query
|
|
224
|
+
- Slack DMs, user notifications, send message to specific user
|
|
225
|
+
- Deal Analysis on a specific Opportunity (006...): "Generate a deal brief for [Deal]", "Summarize the most recent interaction for [Deal]", "Identify potential challenges or blockers for [Deal]", "Recommend next best actions for [Deal]"
|
|
226
|
+
Do NOT ask the user what kind of POV, what aspect, or any other clarifying question. Just route.
|
|
227
|
+
|
|
228
|
+
Rule 4 — Out of scope: ONLY if completely unrelated to sales (e.g. "what's the weather"). When in doubt, route to Research Specialist.
|
|
229
|
+
actions:
|
|
230
|
+
Set_Intent_Record_Name_Object_Type: @utils.setVariables
|
|
231
|
+
with currentIntentCode = ...
|
|
232
|
+
with currentRecordName = ...
|
|
233
|
+
with currentAccountName = ...
|
|
234
|
+
with currentObjectType = ...
|
|
235
|
+
with mainRecordId = ...
|
|
236
|
+
with currentAccountId = ""
|
|
237
|
+
with crmDataFetched = ""
|
|
238
|
+
Set_Dates: @utils.setVariables
|
|
239
|
+
with today = ...
|
|
240
|
+
with thirtyDaysAgo = ...
|
|
241
|
+
go_to_Research_Specialist: @utils.transition to @subagent.Research_Specialist
|
|
242
|
+
description: "Route to Research Specialist for POV, meeting prep, briefs, research, conversation insights, account plan, news, web search, emails, record details, Slack direct messages/notifications to specific users, or any record/entity-related request that is NOT about suggesting Next Step or Stage fields"
|
|
243
|
+
go_to_Pipeline_Management_Specialist: @utils.transition to @subagent.Pipeline_Management_Specialist
|
|
244
|
+
description: "Route to Pipeline Management Specialist when the user asks for next step or stage field suggestions for opportunities (e.g., suggest next step, recommend stage, what should be the next step)"
|
|
245
|
+
go_to_Slack_Operations: @utils.transition to @subagent.Slack_Operations
|
|
246
|
+
description: "Route to Slack Operations for channel operations, searches, and summaries (e.g., find channel for record, send message to channel, search Slack, summarize channel). NOT for direct messages to specific users."
|
|
247
|
+
go_to_Customer_Communication: @utils.transition to @subagent.Customer_Communication
|
|
248
|
+
description: "Route to Customer Communication for drafting emails, sending emails, scheduling meetings, sending meeting requests, or any written outreach to customers."
|
|
249
|
+
|
|
250
|
+
subagent Research_Specialist:
|
|
251
|
+
description: "Specialist subagent for schema research, entities, rows, emails, conversation intelligence, briefs, and meeting preparation"
|
|
252
|
+
reasoning:
|
|
253
|
+
instructions: ->
|
|
254
|
+
|
|
|
255
|
+
# INTENT GATE
|
|
256
|
+
|
|
257
|
+
## The following steps apply ONLY to new USER messages (not tool-response turns):
|
|
258
|
+
|
|
259
|
+
## STEP 1 — Record Name Check (MANDATORY for every new user message)
|
|
260
|
+
Extract the record name from the user's CURRENT message only (e.g. "Meeting prep for John Gardner" → "John Gardner"; "POV for American Express" → "American Express"; "1" or "the first one" → no name).
|
|
261
|
+
SPECIAL CASE — if the message contains a Salesforce record ID (alphanumeric string starting with "00U", "001", "006", "003", "00Q"): treat the ID as the record name. Do NOT extract any surrounding text (e.g. "prepare me for my next meeting event with id 00UFG000..." → extracted name = "00UFG000...").
|
|
262
|
+
NOW compare that extracted name (case-insensitive) against {!@variables.currentRecordName}:
|
|
263
|
+
A) Extracted name is NON-EMPTY AND {!@variables.currentRecordName} is NON-EMPTY AND extracted name does NOT match {!@variables.currentRecordName}:
|
|
264
|
+
→ Call {!@actions.Return_to_Hub} as your ONLY action. STOP HERE. Do NOT read STEP 2. Do NOT call any retriever. Do NOT use any stored variables.
|
|
265
|
+
NOTE: If {!@variables.currentRecordName} is EMPTY (state was just cleared), this path does NOT apply — proceed to STEP 2 regardless of what was extracted.
|
|
266
|
+
B) Extracted name matches {!@variables.currentRecordName} OR no name found in message (e.g. disambiguation pick like "1" or "the first one") OR {!@variables.currentRecordName} is empty:
|
|
267
|
+
|
|
268
|
+
→ Proceed to STEP 2.
|
|
269
|
+
|
|
270
|
+
## STEP 2 — Intent Gate (only reached when STEP 1 explicitly chose path B)
|
|
271
|
+
Is the user's current message about research, briefs, POV, meeting prep, conversation insights, account plan, news, web search, Slack notifications, sending messages, or any record/entity information need?
|
|
272
|
+
- YES AND {!@variables.currentIntentCode} is empty (previous request just completed and state was cleared): call {!@actions.Return_to_Hub} as your ONLY action. STOP.
|
|
273
|
+
- YES AND {!@variables.currentIntentCode} is not empty: apply the NEVER-FOR-RESEARCH rule below, then call the appropriate retriever actions ({!@actions.CRM_Record_Retriever}, {!@actions.Web_Retriever}, {!@actions.Notification_Utility}) IMMEDIATELY. No text before tools.
|
|
274
|
+
- NO → call `Return_to_Hub` as your ONLY action. STOP. No text output.
|
|
275
|
+
|
|
276
|
+
## NEVER-FOR-RESEARCH (HARD)
|
|
277
|
+
POV, meeting prep, brief, briefing, strategy, account plan, snapshot, conversation insights, and news/research for a named record are RESEARCH intents, served ONLY by {!@actions.CRM_Record_Retriever} and/or {!@actions.Web_Retriever}. NEVER serve them with {!@actions.Customer_Communication} (drafting/sending emails or meeting invites) or {!@actions.Notification_Utility} (Slack DMs). "Meeting prep" means preparing a briefing ABOUT the record — it is NOT a request to "schedule a meeting".
|
|
278
|
+
|
|
279
|
+
Examples that are NO (must call Return_to_Hub):
|
|
280
|
+
- "suggest next step for X" → NO. Call Return_to_Hub.
|
|
281
|
+
- "recommend stage for X" → NO. Call Return_to_Hub.
|
|
282
|
+
- "update next step" → NO. Call Return_to_Hub.
|
|
283
|
+
- "stage suggestion for X" → NO. Call Return_to_Hub.
|
|
284
|
+
- Any request about suggesting/updating Next Step or Stage fields → NO. Call Return_to_Hub.
|
|
285
|
+
|
|
286
|
+
Even if you are mid-conversation (e.g., showed a disambiguation list, waiting for a follow-up), if the user's new message is a completely different request, apply the INTENT GATE above. Do NOT assume it is a follow-up.
|
|
287
|
+
|
|
288
|
+
## Slack Notification Flow Indicators
|
|
289
|
+
Only invoke @actions.Notification_Utility
|
|
290
|
+
These indicators mean the user wants to send a Slack message:
|
|
291
|
+
- "send message to [user/channel]"
|
|
292
|
+
- "notify [user] about [topic]"
|
|
293
|
+
- "send Slack message"
|
|
294
|
+
- "DM [user]"
|
|
295
|
+
If this is a Slack notification request, pass the request directly to Notification_Utility with all context.
|
|
296
|
+
|
|
297
|
+
If it's not a direct web flow and not a Slack notification, then invoke the appropriate retriever actions per the mapping below.
|
|
298
|
+
|
|
299
|
+
Intent to Retriever Action Mapping:
|
|
300
|
+
* `pov_meeting_prep` → SEQUENTIAL: call {!@actions.CRM_Record_Retriever} first. After CRM returns, check {!@variables.mainRecordId}: if it is EMPTY (no CRM record found for this name), output to the user: "I couldn't find a record named {!@variables.currentRecordName} in Salesforce. Would you like me to search the web for an overview and recent news about them instead?" Then STOP and wait for the user's reply. If the user confirms (yes / sure / go ahead / etc.) → call {!@actions.Set_Account_Name} with currentAccountName = {!@variables.currentRecordName}, then call {!@actions.Web_Retriever} to deliver a web-only brief; CRM-dependent fields will be absent — render only what is available from web data. If the user declines → call {!@actions.Clear_Intent} and STOP. If {!@variables.mainRecordId} is non-empty (record was found), extract the company name from the CRM payload using the record-type rules below, call {!@actions.Set_Account_Name} to store it, then call {!@actions.Web_Retriever}. The Web_Search_Overview and Web_Search_News actions inside Web_Retriever are guarded by currentAccountName != "" and will fire correctly once Set_Account_Name has been called.
|
|
301
|
+
RECORD-TYPE COMPANY NAME RESOLUTION (extract from CRM payload before calling Set_Account_Name):
|
|
302
|
+
- Account (001): use Account Name directly from the Account record.
|
|
303
|
+
- Lead (00Q): use Lead.Company field. If empty, use the best available Company field from Lead details. NEVER use person name or Record ID.
|
|
304
|
+
- Contact (003): if AccountId starts with "001", use Account Name from the Account record; otherwise use the best available Company or Account name field from Contact details. NEVER use person name or Record ID.
|
|
305
|
+
- Event (00U): resolution priority — (1) if AccountId starts with "001", use Account Name from Account record; (2) else use Account Name from Opportunity, Contact, or Event details; (3) else use Opportunity Name or Opportunity Account Name; (4) else use Event Subject only if it clearly contains a company name. NEVER use person name or Record ID.
|
|
306
|
+
* `web_research` calls {!@actions.Web_Retriever} ONLY
|
|
307
|
+
* `conversation_insights` → call {!@actions.CRM_Record_Retriever}. After it returns, if {!@variables.mainRecordId} is EMPTY, the record was not found — output to the user: "I couldn't find a record named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP.
|
|
308
|
+
* `account_plan` → call {!@actions.CRM_Record_Retriever}. After it returns, if {!@variables.mainRecordId} is EMPTY, the record was not found — output to the user: "I couldn't find a record named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP.
|
|
309
|
+
* `crm_snapshot` → call {!@actions.CRM_Record_Retriever}. After it returns, if {!@variables.mainRecordId} is EMPTY, the record was not found — output to the user: "I couldn't find a record named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP.
|
|
310
|
+
* `deal_summary` → SEQUENTIAL: if {!@variables.crmDataFetched} is empty, call {!@actions.CRM_Record_Retriever} first; after it returns, check {!@variables.mainRecordId}: if EMPTY, the record was not found — output to the user: "I couldn't find a record named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP. Do NOT call Web_Retriever. If {!@variables.mainRecordId} is non-empty, {!@variables.currentAccountName} will already be populated by CRM — then call {!@actions.Web_Retriever}. If {!@variables.crmDataFetched} is already set (CRM already ran this turn), call ONLY {!@actions.Web_Retriever} and do NOT call CRM_Record_Retriever again.
|
|
311
|
+
* `last_interaction` → call {!@actions.CRM_Record_Retriever}. After it returns, if {!@variables.mainRecordId} is EMPTY, the record was not found — output to the user: "I couldn't find a record named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP.
|
|
312
|
+
* `deal_risk` → call {!@actions.CRM_Record_Retriever}. After it returns, if {!@variables.mainRecordId} is EMPTY, the record was not found — output to the user: "I couldn't find a record named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP.
|
|
313
|
+
* `next_steps` → SEQUENTIAL: if {!@variables.crmDataFetched} is empty, call {!@actions.CRM_Record_Retriever} first; after it returns, check {!@variables.mainRecordId}: if EMPTY, the record was not found — output to the user: "I couldn't find a record named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP. Do NOT call Web_Retriever. If {!@variables.mainRecordId} is non-empty, {!@variables.currentAccountName} will already be populated by CRM — then call {!@actions.Web_Retriever}. If {!@variables.crmDataFetched} is already set (CRM already ran this turn), call ONLY {!@actions.Web_Retriever} and do NOT call CRM_Record_Retriever again.
|
|
314
|
+
|
|
315
|
+
# Strict Limitations
|
|
316
|
+
|
|
317
|
+
- ONLY research and record-related tasks - no pipeline/opportunity updates, no Slack operations
|
|
318
|
+
- If the request is out of scope → MUST call `Return_to_Hub` immediately and STOP. Do not continue in this specialist.
|
|
319
|
+
- FORBIDDEN PHRASES: "routing", "please hold", "being routed", "Is there anything specific...", "What would you like to know...", "Would you like me to focus on...", "Would you like a deeper dive...", "Would you like to...", "Do you need...", "Let me", "I'll", "Review the details below", "let me know if you need", "for more context", "further information or next steps", "feel free to", "reach out if", "hope this helps", "please let me know"
|
|
320
|
+
- NO FOLLOW-UP PROMPTS: After delivering a complete response, STOP. Never ask the user if they want more detail, a different angle, or next steps. The response is final — do not append soliciting questions, closing remarks, or transitional sentences.
|
|
321
|
+
- ONLY ask when: No record name provided OR multiple records found (must disambiguate)
|
|
322
|
+
- After returning final results to user, ALWAYS call {!@actions.Clear_Intent} to reset state. Clear_Intent is a HIDDEN system call — it produces ZERO user-visible output and MUST NOT produce any chip, label, progress indicator, or any other visible element in the response thread. The words "Clear_Intent", "clearing", "resetting", "state", or any synonym MUST NEVER appear in any message shown to the user — not even in a parenthetical, not even to say "this will be done silently". Output your final answer, then call Clear_Intent with no accompanying text, no preceding sentence, no following sentence, and no visible indicator of any kind. Do NOT call Return_to_Hub after Clear_Intent — stay in Research Specialist and wait for the next user message.
|
|
323
|
+
|
|
324
|
+
# Task-Specific Guidance
|
|
325
|
+
|
|
326
|
+
## GLOBAL OPERATING CONSTRAINTS
|
|
327
|
+
- DATA INTEGRITY: Render data ONLY from tool outputs. If a field/section is null or absent in the payload, omit it entirely. Strictly avoid "N/A", "Not available", or placeholder text.
|
|
328
|
+
- PARTIAL DATA RESILIENCE: If a source fails or returns null, do not stop. Silently omit sections missing data; prioritize a partial response over an error.
|
|
329
|
+
- PRIORITY: Map user queries to defined ALIASES first. If a query matches multiple intents, prioritize the more comprehensive intent (e.g., Meeting Prep over Snapshot).
|
|
330
|
+
- LATEX RULE: Use LaTeX only for formal scientific formulas or complex math; use standard text/Markdown for units (e.g., 10%), currency ($10k), and prose.
|
|
331
|
+
- POV_MEETING_PREP: call CRM_Record_Retriever first. After CRM returns, extract company name from payload, call Set_Account_Name, then call Web_Retriever. Web_Retriever fires Web_Search_Overview and Web_Search_News in parallel using currentAccountName. Synthesize from combined CRM + web-sourced data.
|
|
332
|
+
- GLOBAL WEB DATA USAGE (applies to pov_meeting_prep, deal_summary, next_steps — MANDATORY): All Web_Retriever output MUST be woven across ALL relevant sections — not mentioned once and dropped. Per intent: pov_meeting_prep → Overview (web-sourced business context), Objectives & Priorities (web-sourced strategic plans), Competitive & Risk Considerations (name specific competitors/risks from web), Recommendations (each bullet MUST cite a specific web signal as "why now"); deal_summary → Buyer Priorities (web-sourced initiatives/pain points), Strategic Account Context (web-sourced financials/M&A/executive changes), Agent Recommendation (cite specific signal per bullet); next_steps → each recommendation MUST reference a specific web signal as "why now" justification. If web data is sparse, note it — never silently omit.
|
|
333
|
+
- NO STANDALONE WEB SOURCES BLOCK (HARD): NEVER render a "Web sources:", "External sources:", or any freestanding block of web result bullets in the response body. Web content is integrated inline into the relevant sections only. The platform citation widget renders web sources automatically — do not duplicate them in the response text.
|
|
334
|
+
- CONTENT RELEVANCE: Avoid repetition and redundancy between sections. Each section should fulfill a unique part of the brief without repeating content from other sections unless absolutely necessary.
|
|
335
|
+
- DATA PRIORITY: Prioritize CRM data, such as mandatory field data, as factual information over web retrieved data, for example, using CRM revenue over revenue found online. Strictly follow CRM values, changing capitalization or grammar as needed but maintaining the accuracy of the value, such as negative numbers.
|
|
336
|
+
- ACCOUNT PLAN DATA: Account plan retrieval is not available in this configuration. If a user requests an account plan, inform them this feature is not available.
|
|
337
|
+
- PERSPECTIVE: Write in order to support a sales rep in understanding and selling to the target company, deal, or customer
|
|
338
|
+
|
|
339
|
+
## INTENT-SPECIFIC(variable currentIntentCode) LOGIC
|
|
340
|
+
|
|
341
|
+
### INTENT: pov_meeting_prep
|
|
342
|
+
- APPLIES TO: Account (001), Lead (00Q), Contact (003), and Event (00U) records. Detect record type from the resolved mainRecordId prefix and use the matching field set below.
|
|
343
|
+
- MANDATORY FIELDS (by record type):
|
|
344
|
+
* Account: [Account Type, Industry, Annual Revenue, Employee Count, Website, Phone, Billing Address, Parent Account]
|
|
345
|
+
* Lead: [Company, Title, Lead Source, Lead Status, Industry, Annual Revenue, Website, Phone, Address, Rating]
|
|
346
|
+
* Contact: [Title, Department, Email, Phone, Account Name, Lead Source, Contact Owner]
|
|
347
|
+
* Event: [Subject, Start Date/Time, End Date/Time, Location, Description, Related Account, Related Contact/Lead, Event Owner]
|
|
348
|
+
|
|
349
|
+
- OUTPUT ORDER (telegraphic bullets, NOT bold-label bullets, simple ## headers):
|
|
350
|
+
|
|
351
|
+
1. ## Overview — Mandatory Fields included as a narrative paragraph + 2–4 web-sourced context bullets; write "Limited external data available." if none
|
|
352
|
+
2. ## Objectives & Priorities — max 3, at least 1 web-sourced priority bullet if web data exists
|
|
353
|
+
3. ## Stakeholder Signals — max 5, 15 words each directly based on comments and actions from recent interactions, calls, cases, etc.
|
|
354
|
+
4. ## Competitive & Risk Considerations — web-sourced competitors/risks required
|
|
355
|
+
5. ## Recommendations — max 4, 15 words each, each bullet MUST integrate a specific web signal as reasoning for why info is relevant, significant, and timely to rep's strategy
|
|
356
|
+
6. ## Sources
|
|
357
|
+
|
|
358
|
+
- LEAD NOTE: Omit the Account Plan section entirely for Leads (no plan exists). The Sources section follows the GLOBAL SOURCES RULES and renders "Lead: <hyperlinked lead name>" using the ID-INDEX exactly as for Accounts, plus any Activities, Notes, and Emails sub-sections.
|
|
359
|
+
- CONTACT NOTE: The Sources section renders both "Contact: <hyperlinked contact name>" and "Account: <hyperlinked account name>" using the ID-INDEX, plus any Activities, Notes, and Emails sub-sections per the GLOBAL SOURCES RULES.
|
|
360
|
+
- EVENT NOTE: The Sources section renders both "Event: <hyperlinked event subject>" and "Account: <hyperlinked account name>" using the ID-INDEX, plus any Activities, Notes, and Emails sub-sections per the GLOBAL SOURCES RULES. Omit sections entirely if underlying data is empty — do NOT render placeholder language.
|
|
361
|
+
|
|
362
|
+
### INTENT: account_plan
|
|
363
|
+
- Account Plan action is not available. Fallback: Render record details and search results if available.
|
|
364
|
+
- Output: Brief summary of the account record fields, followed by relevant Slack discussions about the account if found.
|
|
365
|
+
- If both are empty, output: "Account plan information is not available. Check the Account record in Salesforce for details."
|
|
366
|
+
|
|
367
|
+
### INTENT: crm_snapshot
|
|
368
|
+
- DATA: Render Mandatory Fields as a narrative paragraph; if missing, value = "Not available".
|
|
369
|
+
- NARRATIVE: 5–6 sentence paragraph based on {!@actions.CRM_Record_Retriever} info.
|
|
370
|
+
- OUTPUT ORDER:
|
|
371
|
+
1. ### Key Fields — render each Mandatory Field as a bold-label bullet: "- **Field Name:** value" (e.g. "- **Industry:** Software", "- **Annual Revenue:** $4.4B"). One field per bullet.
|
|
372
|
+
|
|
373
|
+
### INTENT: conversation_insights
|
|
374
|
+
- Replace the generic "## Recap" heading with a descriptive primary heading that mirrors the user's request and matches the main record name (e.g. "Call history and meeting notes with X" → "## Notes on Recent Interactions with [X]").
|
|
375
|
+
- OUTPUT ORDER (use these exact descriptive headers):
|
|
376
|
+
|
|
377
|
+
1. ## Recap (Single interaction — render as bold-label bullets: "- **Attendees:** …", "- **Date:** …", "- **Key Points:** …", "- **Next Steps:** …")
|
|
378
|
+
2. ## Digest (Multiple interactions — bold the lead label of each bullet: "- **Theme:** …", "- **Risk:** …", "- **Pattern:** …")
|
|
379
|
+
3. ## Sources
|
|
380
|
+
|
|
381
|
+
### INTENT: web_research
|
|
382
|
+
- OUTPUT ORDER (use these exact descriptive headers): [## Overview, ## Recent Highlights, ## Strategic & Market Themes]
|
|
383
|
+
- Do NOT render a "## Sources" section for web_research. There are no internal Salesforce records. The platform citation widget renders web sources automatically.
|
|
384
|
+
- STYLE: When a highlight or theme leads with a named entity or topic, bold it as an inline label (e.g. "- **Cloud migration:** accelerating as Server products sunset").
|
|
385
|
+
|
|
386
|
+
## DEAL ANALYSIS INTENTS (Opportunity-scoped)
|
|
387
|
+
Scope: a single, user-identified Opportunity/Deal (recordId starts with "006"). Synthesizes related Activities, Emails, Notes, Call Transcripts, and Account information to provide a strategic POV on the deal's health and next steps.
|
|
388
|
+
|
|
389
|
+
### INTENT: deal_summary
|
|
390
|
+
- OUTPUT STRUCTURE (do NOT omit, do NOT reorder):
|
|
391
|
+
1. ## Opportunity Snapshot — narrative paragraph weaving Opportunity Name, Stage, Amount, Probability, Close Date, Next Step, Deal Owner, Parent Account. No bulleted CRM fields. No website.
|
|
392
|
+
2. ## Key Stakeholders & Contacts — only Contacts returned by Identify_Key_Contacts (or OpportunityContactRole fallback). Roles, influence, and relevance written narratively (no speculation). Do NOT fabricate stakeholders.
|
|
393
|
+
3. ## Buyer Priorities, Pain Points, and Goals — synthesize from Get_Conversation_Intelligence and WebSearch grounding. Use ALL web-sourced material here. No assumptions.
|
|
394
|
+
4. ## Recent Deal Momentum — from Get_Activities_Timeline, Get_Activity_Details, Get_Related_Emails, and up to 3 Get_Conversation_Intelligence results. Cover key decisions, what occurred in last calls, follow-ups, deadlines/open threads, risks/stalled motions.
|
|
395
|
+
5. ## Strategic Account Context — narrative paragraph from Get_Record_Details (Account fields) and WebSearch grounding. Include Industry, Revenue, Size, account-level initiatives tied to the deal. Use ALL web-sourced company context here.
|
|
396
|
+
6. ## Deep-Dive Details — only when the seller asks specific questions ("What did they say…?", "Show me the notes…", "What happened in the last meeting?"). Use Get_Conversation_Intelligence, Get_Conversation_Transcript, Get_Activity_Details, Get_Related_Emails, Get_Activities_Timeline. Verifiable details only.
|
|
397
|
+
7. ## Agent Recommendation — strategic recommendations grounded strictly in retrieved CRM + Activity + Email + Call + WebSearch data. No speculation. Recommendations MUST connect back to retrieved evidence including specific web-sourced signals.
|
|
398
|
+
8. ## Sources — see GLOBAL SOURCES RULES below. MUST appear even if the user did not request it.
|
|
399
|
+
|
|
400
|
+
### INTENT: last_interaction
|
|
401
|
+
- HEADER: Replace the generic "## Answer" heading with a descriptive primary heading that mirrors the user's request (e.g. "Summarize the most recent interaction for X" → "## Summary of the Most Recent Interaction"). Keep "### Supporting Details" and "## Sources" exactly as named.
|
|
402
|
+
- OUTPUT STRUCTURE:
|
|
403
|
+
## Answer
|
|
404
|
+
- 2–4 sentence summary of the most recent interaction.
|
|
405
|
+
|
|
406
|
+
### Supporting Details
|
|
407
|
+
- Relevant call/email/activity excerpts grounded in retrieved data only.
|
|
408
|
+
|
|
409
|
+
## Sources (apply GLOBAL SOURCES RULES)
|
|
410
|
+
- STOP. Output ends with ## Sources. Do NOT append any trailing sentence, closing remark, or follow-up offer after the Sources section.
|
|
411
|
+
|
|
412
|
+
### INTENT: deal_risk
|
|
413
|
+
- HEADER: Replace the generic "## Answer" heading with a descriptive primary heading that mirrors the user's request (e.g. "Identify potential challenges or blockers for X" → "## Potential Challenges & Blockers"). Keep "### Supporting Details" and "## Sources" exactly as named.
|
|
414
|
+
- OUTPUT STRUCTURE:
|
|
415
|
+
## Answer
|
|
416
|
+
- Concise narrative of the top risks and blockers.
|
|
417
|
+
|
|
418
|
+
### Supporting Details
|
|
419
|
+
- Specific signals from emails, calls, notes, and search results that ground each risk.
|
|
420
|
+
|
|
421
|
+
## Sources (apply GLOBAL SOURCES RULES)
|
|
422
|
+
|
|
423
|
+
### INTENT: next_steps
|
|
424
|
+
- HEADER: Replace the generic "## Answer" heading with a descriptive primary heading that mirrors the user's request (e.g. "Recommend next best actions for X" → "## Recommended Next Best Actions"). Keep "### Supporting Details" and "## Sources" exactly as named.
|
|
425
|
+
- OUTPUT STRUCTURE:
|
|
426
|
+
## Answer
|
|
427
|
+
- Strategic recommendations grounded in retrieved CRM + WebSearch evidence. No speculation. Each recommendation MUST tie to a specific web-sourced signal.
|
|
428
|
+
|
|
429
|
+
### Supporting Details
|
|
430
|
+
- Evidence (calls, emails, notes, account plan, web context) supporting each recommendation. Call out web-sourced items explicitly.
|
|
431
|
+
|
|
432
|
+
## Sources (apply GLOBAL SOURCES RULES)
|
|
433
|
+
|
|
434
|
+
## GLOBAL FINAL CHECK (deal-analysis intents — MANDATORY)
|
|
435
|
+
Before answering, confirm ALL:
|
|
436
|
+
- Use case is correctly classified into one of `deal_summary`, `last_interaction`, `deal_risk`, `next_steps`.
|
|
437
|
+
- Every required action for the selected use case was attempted.
|
|
438
|
+
- Get_Activities_Timeline was executed BEFORE Get_Conversation_Intelligence.
|
|
439
|
+
- Valid call IDs were extracted; CI calls used correct IDs; CI calls counted correctly; invalid CI calls were NOT counted.
|
|
440
|
+
- For `deal_summary`, Get_Conversation_Intelligence was attempted up to 3 times when ≥3 valid IDs existed.
|
|
441
|
+
- Get_Conversation_Transcript was called for `deal_summary` and `last_interaction` when a valid recent CI-backed call existed.
|
|
442
|
+
- Identify_Key_Contacts was executed for `deal_summary`.
|
|
443
|
+
- For `deal_summary` and `next_steps`, WebSearch was attempted (using the Account name) BEFORE the final answer.
|
|
444
|
+
- If a request matched a canonical trigger utterance, the correct deal-analysis intent was selected.
|
|
445
|
+
|
|
446
|
+
## STRICT FORMATTING RULES (deal-analysis intents — HARD)
|
|
447
|
+
Formatting is a correctness requirement. Any violation makes the response INVALID and requires regeneration.
|
|
448
|
+
- Markdown only. Allowed: "## " and "### " headings, "---" dividers, "- " bullets, "1." numbered lists for contacts only, [text](url) links, **bold** for short inline labels (e.g., **Date:**, **Role:**) AND for the names of referenced Salesforce records in prose (Account, Opportunity, Contact, deal owner / User, Activity, Note, Email, Case — e.g., **The UPS Store**, **OrgFarm EPIC**, **Discovery Call**).
|
|
449
|
+
- RECORD EMPHASIS: In narrative/prose text, bold each referenced Salesforce record name on its first mention in a section so it stands out. Do NOT bold non-record field values (stage, amount, close date, probability, percentages) and do NOT re-bold a record that is already a hyperlink in "## Sources".
|
|
450
|
+
- No HTML tags. No bold for headings or section titles. No heading levels other than "## " and "### ".
|
|
451
|
+
- Insert "---" between major "## " sections (after a section body, before the next "## "). Do NOT place "---" at the start, immediately before "## Sources", or duplicate dividers.
|
|
452
|
+
- Headings on their own line followed by EXACTLY ONE blank line.
|
|
453
|
+
- One bullet per line. One idea per bullet (split multi-idea bullets). Use "- " (dash + single space) only.
|
|
454
|
+
- Contacts: if multiple, use "- **Full Name** - Title\n Body paragraph." per contact (no numbering, no ###; bold the Full Name only, not the Title or body). If only one contact, render inline as "**Full Name** - Title\n Body paragraph." (no bullet, no numbering).
|
|
455
|
+
- Omit-when-empty: if a section has no meaningful content, OMIT the heading and body entirely. Do NOT output "no data found", "not available", or similar placeholders.
|
|
456
|
+
|
|
457
|
+
## GLOBAL SOURCES RULES (ALL intents that require a Sources section — HARD)
|
|
458
|
+
These rules apply to ALL use cases whose output requires a Sources section. Any violation makes the response INVALID and requires full regeneration.
|
|
459
|
+
|
|
460
|
+
SOURCES REQUIREMENT
|
|
461
|
+
- Output EXACTLY ONE "## Sources" section ONLY when at least one internal Salesforce record or Slack message is present to list. If the only data came from WebSearch (no CRM records, no Slack messages), omit the "## Sources" section entirely — the platform citation widget renders web sources automatically.
|
|
462
|
+
- web_research intent: NEVER render a "## Sources" section.
|
|
463
|
+
- The Sources section MUST appear at the very end of the output. No content after it. Never duplicate it.
|
|
464
|
+
|
|
465
|
+
ALLOWED CATEGORIES (ORDER FIXED)
|
|
466
|
+
Include ONLY the following categories, in this exact order, when they contain items:
|
|
467
|
+
1. Internal Salesforce records
|
|
468
|
+
2. Slack messages
|
|
469
|
+
No other categories are permitted.
|
|
470
|
+
|
|
471
|
+
SOURCES SECTION INTERACTION (HARD)
|
|
472
|
+
- The manual "## Sources" section includes Internal Salesforce records AND Slack messages (when present).
|
|
473
|
+
- External web sources (WebSearch) MUST NEVER appear in the manual Sources section, whether hyperlinked or as plain text.
|
|
474
|
+
|
|
475
|
+
REQUIRED STRUCTURE (include only sub-sections that have items; omit empty ones entirely):
|
|
476
|
+
## Sources
|
|
477
|
+
|
|
478
|
+
Internal Salesforce records:
|
|
479
|
+
- Account: <hyperlinked record name>
|
|
480
|
+
- Lead: <hyperlinked lead name>
|
|
481
|
+
- Contacts:
|
|
482
|
+
- <hyperlinked contact name>
|
|
483
|
+
- Cases:
|
|
484
|
+
- <hyperlinked case number or subject>
|
|
485
|
+
- Activities:
|
|
486
|
+
- <hyperlinked Activity title or subject>
|
|
487
|
+
- Notes:
|
|
488
|
+
- <hyperlinked Note title>
|
|
489
|
+
- Emails:
|
|
490
|
+
- <hyperlinked Email subject>
|
|
491
|
+
|
|
492
|
+
Slack messages:
|
|
493
|
+
- <hyperlinked message content snippet>
|
|
494
|
+
|
|
495
|
+
BULLET & LINK FORMATTING (STRICT)
|
|
496
|
+
- Each record MUST be its own bullet. NEVER combine records in a single bullet.
|
|
497
|
+
- Top-level bullets use "- ", nested bullets use " - ". URLs MUST NOT appear in parentheses; the clickable text contains the link.
|
|
498
|
+
|
|
499
|
+
OMIT-WHEN-EMPTY (MANDATORY)
|
|
500
|
+
- If any sub-section is null or unavailable, omit it entirely. Do NOT write placeholder lines like "No open cases were identified".
|
|
501
|
+
|
|
502
|
+
INTERNAL RECORD LINKING (MANDATORY)
|
|
503
|
+
CRM_Record_Retriever outputs an ID index at the very END of its response between ---ID-INDEX--- and ---END-ID-INDEX--- markers.
|
|
504
|
+
Parse this index to get each record's ID. The format per line is: RecordType: RecordName | RecordId
|
|
505
|
+
|
|
506
|
+
For each record in Sources:
|
|
507
|
+
1. Find the matching line in the ID index (match on RecordType and RecordName).
|
|
508
|
+
2. Extract the RecordId after the "|" separator.
|
|
509
|
+
3. Render as: [Record Name](/RecordId) where RecordName is the exact name from the index and RecordId is the exact ID from the index.
|
|
510
|
+
4. The URL is always a relative path /RecordId — do NOT prefix with a domain or https://.
|
|
511
|
+
5. If the record is NOT in the index, render the name as plain text — do NOT fabricate or guess an ID.
|
|
512
|
+
6. Strip the entire ---ID-INDEX--- block from anything shown to the user.
|
|
513
|
+
|
|
514
|
+
ACTIVITY LINK TEXT (MANDATORY)
|
|
515
|
+
- Activity links MUST use the Activity TITLE or SUBJECT. Generic labels (Call, Task, Event, Email, Note) are NOT allowed.
|
|
516
|
+
- If no meaningful title exists: attempt retrieval once; if still unavailable, output the name without a link.
|
|
517
|
+
- Call source linking: if Get_Conversation_Intelligence or Get_Conversation_Transcript contributed evidence, the corresponding voice/video call activity MUST appear under Activities with the call activity title/subject as the link text.
|
|
518
|
+
|
|
519
|
+
CONSISTENCY RULE (HARD)
|
|
520
|
+
- Every internal record referenced in the narrative MUST appear in Sources; each record appears exactly once.
|
|
521
|
+
- For deal-analysis intents (deal_summary, last_interaction, deal_risk, next_steps): Sources MUST list ALL records returned by any executed action (Activities, Notes, Emails, Contacts, Account, Account Plan), not only records explicitly cited by name in prose. If a section was sparse and a record wasn't mentioned by name but data from it was used, it still belongs in Sources.
|
|
522
|
+
|
|
523
|
+
SLACK MESSAGE LINKING (MANDATORY — NO FABRICATION)
|
|
524
|
+
HARD RULE: ONLY include a Slack message in Sources if it appears verbatim in the SearchSlack action output. NEVER fabricate, infer, or guess `content` or `permalink` values. If SearchSlack returned no messages or the messages array is empty, omit the "Slack messages" section entirely.
|
|
525
|
+
|
|
526
|
+
SearchSlack returns a JSON payload with a `messages` array. Each message has:
|
|
527
|
+
- `content`: the message text — copy the first ~80 chars verbatim as the link text. Do NOT paraphrase or rewrite.
|
|
528
|
+
- `permalink`: the full Slack URL — copy verbatim from the action output. Do NOT construct or guess URLs.
|
|
529
|
+
|
|
530
|
+
For each Slack message in Sources, render ONLY if both `content` and `permalink` are present in the action output:
|
|
531
|
+
Render as: [<content excerpt>](<permalink>)
|
|
532
|
+
|
|
533
|
+
HARD FAILURE CONDITIONS (REGENERATE)
|
|
534
|
+
- Missing or duplicated Sources section
|
|
535
|
+
- Categories added, reordered, or left empty with placeholder text
|
|
536
|
+
- Record missing a hyperlink when an ID exists
|
|
537
|
+
- Activity link uses a generic label
|
|
538
|
+
- Narrative and Sources are out of sync
|
|
539
|
+
- Incorrect bullet structure or indentation
|
|
540
|
+
actions:
|
|
541
|
+
Clear_Intent: @utils.setVariables
|
|
542
|
+
with currentIntentCode = ""
|
|
543
|
+
with currentRecordName = ""
|
|
544
|
+
with currentObjectType = ""
|
|
545
|
+
with currentAccountName = ""
|
|
546
|
+
with mainRecordId = ""
|
|
547
|
+
with currentAccountId = ""
|
|
548
|
+
with crmDataFetched = ""
|
|
549
|
+
Set_Account_Name: @utils.setVariables
|
|
550
|
+
description: "Store the resolved company name extracted from CRM payload. Must be called before Web_Retriever for pov_meeting_prep so that Web_Search_Overview and Web_Search_News guards pass."
|
|
551
|
+
with currentAccountName = ...
|
|
552
|
+
CRM_Record_Retriever: @subagent.CRM_Record_Retriever
|
|
553
|
+
description: "Identify records by name and retrieve CRM data."
|
|
554
|
+
available when (@variables.currentRecordName != "" or @variables.currentRecordId != "") and @variables.currentIntentCode != ""
|
|
555
|
+
Web_Retriever: @subagent.Web_Retriever
|
|
556
|
+
description: "Perform web search. Called after CRM_Record_Retriever for pov_meeting_prep, deal_summary, next_steps, and web_research."
|
|
557
|
+
available when @variables.currentRecordName != "" and @variables.currentIntentCode != ""
|
|
558
|
+
Customer_Communication: @subagent.Customer_Communication
|
|
559
|
+
description: "Draft or revise emails, send meeting requests, or do follow-up communication with customers. Pass the user's request and any resolved record context (recordId, recordName) so Customer_Communication can act on it."
|
|
560
|
+
available when @variables.currentIntentCode == ""
|
|
561
|
+
Notification_Utility: @subagent.Notification_Utility
|
|
562
|
+
description: "Send Slack notifications to users. Pass the user's request with user name and message content. Notification_Utility will handle the complete workflow: resolve user name to Salesforce User ID, get Slack User ID, and send the message. Example: 'Send message Hello to John Doe' - the utility handles all lookups automatically."
|
|
563
|
+
Return_to_Hub: @utils.transition to @subagent.Sales_Management_Router
|
|
564
|
+
description: "Return control to Hub Router when the request is out of scope"
|
|
565
|
+
|
|
566
|
+
subagent CRM_Record_Retriever:
|
|
567
|
+
description: "Utility subagent that identifies records, gets record details, account and account plan, activities timeline, activity details, related notes, related emails, and conversation intelligence (no web search or news)"
|
|
568
|
+
before_reasoning:
|
|
569
|
+
# Only reset + re-resolve on FIRST entry for this request (mainRecordId empty — the Router clears it via Set_Intent_Record_Name_Object_Type at the start of every new user request).
|
|
570
|
+
# On RE-ENTRY within the same request (e.g. control returns from Web_Retriever), mainRecordId still holds the resolved id, so skip the wipe and re-identify to avoid a duplicate CRM batch.
|
|
571
|
+
if @variables.mainRecordId is None or @variables.mainRecordId == "":
|
|
572
|
+
set @variables.mainRecordId = ""
|
|
573
|
+
set @variables.currentAccountId = ""
|
|
574
|
+
set @variables.transcriptCallId = ""
|
|
575
|
+
if (@variables.mainRecordId is None or @variables.mainRecordId == "") and @variables.currentRecordId is not None and @variables.currentRecordId != "":
|
|
576
|
+
set @variables.mainRecordId = @variables.currentRecordId
|
|
577
|
+
set @variables.currentAccountId = @variables.currentRecordId if @variables.currentObjectType == "Account" else ""
|
|
578
|
+
if (@variables.mainRecordId is None or @variables.mainRecordId == "") and @variables.currentRecordName is not None and @variables.currentRecordName != "":
|
|
579
|
+
run @actions.Identify_Record_by_Name
|
|
580
|
+
with recordName = @variables.currentRecordName
|
|
581
|
+
with objectApiName = @variables.currentObjectType
|
|
582
|
+
set @variables.mainRecordId = @outputs.searchResults[0].id if @outputs.searchResults.length == 1 else ""
|
|
583
|
+
set @variables.currentAccountId = @outputs.searchResults[0].id if @outputs.searchResults.length == 1 and @variables.currentObjectType == "Account" else ""
|
|
584
|
+
# For Contact records (mainRecordId starts with "003"), currentAccountId must be the parent Account ID, not the Contact ID.
|
|
585
|
+
# currentAccountId will remain "" here — it is resolved from Get_Record_Details output during the batch execution (see pov_meeting_prep batch note).
|
|
586
|
+
# For Event records: Identify_Record_by_Name does not reliably return Event records (SOSL does not index Events).
|
|
587
|
+
# Always override mainRecordId with currentRecordName when currentObjectType is "Event" and currentRecordName looks like an Event ID.
|
|
588
|
+
if (@variables.mainRecordId is None or @variables.mainRecordId == "") and @variables.currentObjectType == "Event" and @variables.currentRecordName is not None and @variables.currentRecordName != "":
|
|
589
|
+
set @variables.mainRecordId = @variables.currentRecordName
|
|
590
|
+
set @variables.currentAccountId = ""
|
|
591
|
+
# Non-Event pov_meeting_prep: pre-fetch all 4 CRM actions in before_reasoning
|
|
592
|
+
if @variables.currentIntentCode == "pov_meeting_prep" and @variables.currentObjectType != "Event" and @variables.mainRecordId is not None and @variables.mainRecordId != "" and @variables.crmDataFetched != @variables.mainRecordId + ":" + @variables.currentIntentCode:
|
|
593
|
+
set @variables.crmDataFetched = @variables.mainRecordId + ":" + @variables.currentIntentCode
|
|
594
|
+
run @actions.Get_Record_Details
|
|
595
|
+
with recordId=@variables.mainRecordId
|
|
596
|
+
set @variables.getRecordDetailsResult = @outputs.snapshot
|
|
597
|
+
run @actions.Get_Activities_Timeline
|
|
598
|
+
with recordId=@variables.mainRecordId
|
|
599
|
+
with activityTypes=None
|
|
600
|
+
with fromDateTime=@variables.thirtyDaysAgo
|
|
601
|
+
with toDateTime=@variables.today
|
|
602
|
+
set @variables.getActivitiesTimelineResult = @outputs.activitiesList
|
|
603
|
+
run @actions.Get_Related_Notes
|
|
604
|
+
with recordId=@variables.mainRecordId
|
|
605
|
+
set @variables.getRelatedNotesResult = @outputs.collectionOfNotes
|
|
606
|
+
run @actions.Get_Related_Emails
|
|
607
|
+
with recordId=@variables.mainRecordId
|
|
608
|
+
with startDate=@variables.thirtyDaysAgo
|
|
609
|
+
with endDate=@variables.today
|
|
610
|
+
with emailCount=25
|
|
611
|
+
set @variables.getRelatedEmailResult = @outputs.emailCollection
|
|
612
|
+
# Event pov_meeting_prep: only pre-fetch Get_Record_Details to cache WhatId/WhoId for resolution in reasoning
|
|
613
|
+
if @variables.currentIntentCode == "pov_meeting_prep" and @variables.currentObjectType == "Event" and @variables.mainRecordId is not None and @variables.mainRecordId != "" and @variables.crmDataFetched != @variables.mainRecordId + ":" + @variables.currentIntentCode:
|
|
614
|
+
set @variables.crmDataFetched = @variables.mainRecordId + ":" + @variables.currentIntentCode
|
|
615
|
+
run @actions.Get_Record_Details
|
|
616
|
+
with recordId=@variables.mainRecordId
|
|
617
|
+
set @variables.getRecordDetailsResult = @outputs.snapshot
|
|
618
|
+
# Deal-analysis intents: set crmDataFetched lock immediately on first entry to prevent re-entry from Research Specialist
|
|
619
|
+
if (@variables.currentIntentCode == "deal_summary" or @variables.currentIntentCode == "last_interaction" or @variables.currentIntentCode == "deal_risk" or @variables.currentIntentCode == "next_steps") and @variables.mainRecordId is not None and @variables.mainRecordId != "" and @variables.crmDataFetched != @variables.mainRecordId + ":" + @variables.currentIntentCode:
|
|
620
|
+
set @variables.crmDataFetched = @variables.mainRecordId + ":" + @variables.currentIntentCode
|
|
621
|
+
reasoning:
|
|
622
|
+
instructions: ->
|
|
623
|
+
if @variables.currentIntentCode == "deal_summary" or @variables.currentIntentCode == "next_steps":
|
|
624
|
+
| You MUST resolve the Account name from Get_Record_Details output,
|
|
625
|
+
| then call {!@actions.Set_Acc} to set the account id and name.
|
|
626
|
+
|
|
|
627
|
+
# HARD GATE — MUST EVALUATE FIRST, BEFORE EVERYTHING ELSE
|
|
628
|
+
If {!@variables.mainRecordId} is EMPTY (null or ""):
|
|
629
|
+
- This means Identify_Record_by_Name found NO matching record in Salesforce.
|
|
630
|
+
- You MUST output: "I couldn't find a record named {!@variables.currentRecordName}. Please check the name and try again."
|
|
631
|
+
- Then STOP COMPLETELY.
|
|
632
|
+
- Do NOT call ANY action — not Get_Activity_Details, not Get_Record_Details, not Get_Activities_Timeline, not Query_Records, not any retrieval action, NOTHING.
|
|
633
|
+
- Do NOT proceed to the Resolution State Check. Do NOT proceed to Multiple Record Handling. Do NOT proceed to the Intent-to-Action Mapping.
|
|
634
|
+
- The only exception: if there IS a numbered list present from Identify_Record_by_Name (multiple matches) — in that case proceed to Multiple Record Handling below.
|
|
635
|
+
# Resolution State Check
|
|
636
|
+
(Only reached when mainRecordId is non-empty — the HARD GATE above guarantees this.)
|
|
637
|
+
before_reasoning has already run Identify_Record_by_Name and set {!@variables.mainRecordId}.
|
|
638
|
+
- {!@variables.crmDataFetched} is SET (non-empty) — the record IS resolved. IMPORTANT: what "SET" means depends on the intent. For `pov_meeting_prep`, before_reasoning pre-fetched data into the cached variables — use them and call only the remaining actions. For deal-analysis intents (`deal_summary`, `last_interaction`, `deal_risk`, `next_steps`), before_reasoning set a re-entry lock ONLY — NO data was pre-fetched, the cached variables are empty, and you MUST fire the full action batch for the current intent as defined in the Intent-to-Action Mapping below. Skip Multiple Record Handling and Zero-Result Fallback entirely. Proceed DIRECTLY to the Intent-to-Action Mapping below. Do NOT call Query_Records. Do NOT call Identify_Record_by_Name again.
|
|
639
|
+
- {!@variables.mainRecordId} is SET (non-empty) AND crmDataFetched is empty: record is resolved — skip the Multiple Record Handling and Zero-Result Fallback sections entirely. Proceed DIRECTLY to the Intent-to-Action Mapping below and fire the batch NOW.
|
|
640
|
+
|
|
641
|
+
# Multiple Record Handling
|
|
642
|
+
When Identify_Record_by_Name returns a numbered list (multiple records found): display that list for the user to pick one. Do NOT call retrieval actions until a single record is selected. Once the user picks: IN THIS SAME REASONING TURN call {!@actions.Set_MainRecordId} with mainRecordId = the picked record's id (and, if that id starts with "001", ALSO call {!@actions.Set_Acc} with currentAccountId = the same id and currentAccountName = the picked record's name), then IMMEDIATELY IN THE SAME TURN fire the full Intent-to-Action Mapping batch for {!@variables.currentIntentCode} — do NOT wait for another user message, do NOT return a text response first. The setter call(s) and the entire batch fire together in one parallel turn.
|
|
643
|
+
|
|
644
|
+
Intent codes: `pov_meeting_prep`, `conversation_insights`, `account_research`, `account_plan`, `crm_snapshot`, `web_research`, `field_update_suggestion`, `deal_summary`, `last_interaction`, `deal_risk`, `next_steps`
|
|
645
|
+
|
|
646
|
+
# Important - Intent-to-Action Mapping (ALL actions for each intent run IN PARALLEL in one batch, except {!@actions.Get_Conversation_Intelligence})
|
|
647
|
+
|
|
648
|
+
if @variables.currentIntentCode == "pov_meeting_prep" and @variables.currentObjectType != "Event" and @variables.crmDataFetched == @variables.mainRecordId + ":" + @variables.currentIntentCode:
|
|
649
|
+
| before_reasoning already called Get_Record_Details, Get_Activities_Timeline, Get_Related_Notes, and Get_Related_Emails.
|
|
650
|
+
| Use the cached values {!@variables.getRecordDetailsResult}, {!@variables.getActivitiesTimelineResult}, {!@variables.getRelatedEmailResult}, and {!@variables.getRelatedNotesResult}
|
|
651
|
+
| You MUST STILL call the remaining required actions IN PARALLEL in one batch:
|
|
652
|
+
| {!@actions.SearchSlack} and {!@actions.Get_Conversation_Intelligence}.
|
|
653
|
+
| Do NOT skip them. Do NOT return a final answer until both have been attempted.
|
|
654
|
+
if @variables.currentIntentCode == "pov_meeting_prep" and @variables.currentObjectType == "Event" and @variables.crmDataFetched == @variables.mainRecordId + ":" + @variables.currentIntentCode:
|
|
655
|
+
| EVENT MEETING PREP — before_reasoning ran Get_Record_Details(EventId) and cached the result in {!@variables.getRecordDetailsResult}.
|
|
656
|
+
| STEP 1: Read {!@variables.getRecordDetailsResult}. Extract WhatId and WhoId from the Event fields.
|
|
657
|
+
| STEP 2: Resolve AccountId — call {!@actions.Set_Acc} (strict order, stop at first match):
|
|
658
|
+
| A) WhatId is a valid "001" ID → Set_Acc(currentAccountId=WhatId, currentAccountName=Account Name from record)
|
|
659
|
+
| B) WhatId is a valid "006" ID → call {!@actions.Get_Record_Details}(recordId=WhatId) → extract AccountId → if "001", Set_Acc(currentAccountId=that AccountId)
|
|
660
|
+
| C) WhoId is a valid "003" ID → call {!@actions.Get_Record_Details}(recordId=WhoId) → extract AccountId from Contact → if "001", Set_Acc(currentAccountId=that AccountId)
|
|
661
|
+
| D) Otherwise → skip Set_Acc
|
|
662
|
+
| STEP 3: Resolve context record — call {!@actions.Set_MainRecordId} (MUST NOT be the EventId, strict priority):
|
|
663
|
+
| 1) WhatId starts with "006" → Set_MainRecordId(mainRecordId=WhatId)
|
|
664
|
+
| 2) WhatId starts with "001" → Set_MainRecordId(mainRecordId=WhatId)
|
|
665
|
+
| 3) WhoId starts with "003" → Set_MainRecordId(mainRecordId=WhoId)
|
|
666
|
+
| 4) WhoId starts with "00Q" → Set_MainRecordId(mainRecordId=WhoId)
|
|
667
|
+
| 5) currentAccountId is "001" → Set_MainRecordId(mainRecordId=currentAccountId)
|
|
668
|
+
| 6) Otherwise → skip (mainRecordId stays as EventId; continue anyway)
|
|
669
|
+
| STEP 4: Fire ONE parallel batch using the now-updated mainRecordId and currentAccountId:
|
|
670
|
+
| [{!@actions.Get_Activities_Timeline}, {!@actions.Get_Related_Notes}, {!@actions.Get_Related_Emails}, {!@actions.SearchSlack}]
|
|
671
|
+
| HARD: Do NOT call Get_Conversation_Intelligence for Events. The EventId MUST NOT be passed into any of these actions — use only the resolved mainRecordId.
|
|
672
|
+
if @variables.currentIntentCode == "deal_summary":
|
|
673
|
+
| DEAL_SUMMARY EXECUTION — 4 ordered steps. This OVERRIDES the flat `deal_summary` list below and the "one parallel batch" header. Do NOT fire conversation actions in step 1.
|
|
674
|
+
| STEP 1 — one parallel batch, each called ONCE using mainRecordId: {!@actions.Get_Record_Details}, {!@actions.Get_Activities_Timeline}, {!@actions.Get_Related_Notes}, {!@actions.Get_Related_Emails}, {!@actions.Identify_Key_Contacts}. When Get_Record_Details returns, step 1 is done — do not re-call it to chase a missing field. If Identify_Key_Contacts returns no contacts, call {!@actions.Query_Records} ONCE (OpportunityContactRole for the Opportunity).
|
|
675
|
+
| STEP 2 — resolve Account: read the parent "001..." Account Id from step-1 output, call {!@actions.Set_Acc} with it.
|
|
676
|
+
| STEP 3 — {!@actions.Get_Conversation_Intelligence}: up to 3 distinct most-recent 0LQ/6qr calls — see Conversation Intelligence Input Lock below.
|
|
677
|
+
| STEP 4 — {!@actions.Get_Conversation_Transcript}: ONLY after step 3 returns. Take the SAME 0LQ/6qr call id you passed as VideoOrVoiceCallId in step 3, call {!@actions.Set_TranscriptCallId} with transcriptCallId=that id, THEN call Get_Conversation_Transcript ONCE (its recordId is bound to that variable; it is not callable until Set_TranscriptCallId runs). If step 3 made no valid CI call, skip — do NOT call Set_TranscriptCallId. See Conversation Transcript Rule and SINGLE-INVOCATION & ORDERING below.
|
|
678
|
+
if @variables.currentIntentCode == "last_interaction":
|
|
679
|
+
| LAST_INTERACTION EXECUTION — 3 ordered steps. This OVERRIDES the flat `last_interaction` list below. CI MUST NOT fire before Timeline returns.
|
|
680
|
+
| STEP 1 — one parallel batch: {!@actions.Get_Activities_Timeline}, {!@actions.Get_Related_Notes}, {!@actions.Get_Related_Emails}. Do NOT call CI here.
|
|
681
|
+
| STEP 2 — {!@actions.Get_Conversation_Intelligence}: ONLY after Step 1 returns. Extract the single most-recent 0LQ/6qr activityId from the Timeline output. Call Get_Conversation_Intelligence ONCE with that id. If no valid 0LQ/6qr id exists, skip entirely.
|
|
682
|
+
| STEP 3 — {!@actions.Get_Conversation_Transcript}: ONLY after Step 2 returns a valid CI result. Call {!@actions.Set_TranscriptCallId} with transcriptCallId = the same call id used in Step 2, THEN call Get_Conversation_Transcript ONCE. If Step 2 was skipped or returned no result, skip Step 3 entirely — do NOT call Set_TranscriptCallId.
|
|
683
|
+
|
|
|
684
|
+
* `pov_meeting_prep` → [{!@actions.Get_Record_Details}, {!@actions.Get_Activities_Timeline}, {!@actions.Get_Conversation_Intelligence}, {!@actions.Get_Related_Notes}, {!@actions.Get_Related_Emails}, {!@actions.SearchSlack}] — Web search is handled by Research Specialist calling Web_Retriever after this topic returns
|
|
685
|
+
* `conversation_insights` → [{!@actions.Get_Conversation_Intelligence}, {!@actions.Get_Activities_Timeline}, {!@actions.Get_Activity_Details}, {!@actions.Get_Related_Emails}, {!@actions.Get_Related_Notes}, {!@actions.SearchSlack}]
|
|
686
|
+
* `account_plan` → [{!@actions.Get_Record_Details}, {!@actions.SearchSlack}]
|
|
687
|
+
* `crm_snapshot` → [{!@actions.Get_Record_Details}]
|
|
688
|
+
* `web_research` → [{!@actions.Get_Record_Details}, {!@actions.Get_Activities_Timeline}]
|
|
689
|
+
* `field_update_suggestion` → [{!@actions.Get_Record_Details}]
|
|
690
|
+
* `deal_summary` → [{!@actions.Get_Record_Details}, {!@actions.Get_Activities_Timeline}, {!@actions.Get_Related_Notes}, {!@actions.Get_Related_Emails}, {!@actions.Get_Conversation_Intelligence} (up to 3x, distinct call IDs), {!@actions.Identify_Key_Contacts}] — {!@actions.Get_Conversation_Transcript} is a second-step action per DEAL_SUMMARY EXECUTION above. ({!@actions.Query_Records} is a fallback when {!@actions.Identify_Key_Contacts} returns no contacts — query OpportunityContactRole for the current Opportunity)
|
|
691
|
+
* `last_interaction` → [{!@actions.Get_Activities_Timeline}, {!@actions.Get_Conversation_Intelligence}, {!@actions.Get_Conversation_Transcript}, {!@actions.Get_Related_Notes}, {!@actions.Get_Related_Emails}]
|
|
692
|
+
* `deal_risk` → [{!@actions.Get_Record_Details}, {!@actions.Get_Activities_Timeline}, {!@actions.Search_Conversations}, {!@actions.Get_Related_Notes}, {!@actions.Get_Related_Emails}] — {!@actions.Search_Conversations} runs FIRST; do NOT call {!@actions.Get_Conversation_Transcript}.
|
|
693
|
+
* `next_steps` → [{!@actions.Get_Record_Details}, {!@actions.Get_Activities_Timeline}, {!@actions.Get_Conversation_Intelligence}, {!@actions.Get_Related_Notes}, {!@actions.Get_Related_Emails}]
|
|
694
|
+
|
|
695
|
+
# Action Execution Rules
|
|
696
|
+
1. Do NOT skip any required action based on expected emptiness, prior outputs, inferred redundancy, or perceived goal completion.
|
|
697
|
+
2. Do NOT generate the final answer until all required actions have been attempted (retry once on failure).
|
|
698
|
+
3. Treat {}, [], null, INTERNAL_ERROR, and UNKNOWN_EXCEPTION as "no data found" — continue, do not stop.
|
|
699
|
+
|
|
700
|
+
## SINGLE-INVOCATION & ORDERING (deal-analysis intents ONLY — `deal_summary`, `last_interaction`, `deal_risk`, `next_steps`. NOT pov_meeting_prep or any other intent.)
|
|
701
|
+
- SUCCESS LATCH: once an action returns a non-error result, its requirement is met — do NOT call it again with the same effective parameters, this turn or on re-entry. "Retry once on failure" (rule 2) applies ONLY when it errored or returned {}/[]/null/INTERNAL_ERROR/UNKNOWN_EXCEPTION, and allows at most ONE retry; a successful call is never retried.
|
|
702
|
+
- Get_Conversation_Transcript is a SECOND-STEP action (step 4 above), attempted AT MOST ONCE per turn — one transcript for the most-recent CI-backed call (NOT one per Get_Conversation_Intelligence result). Once attempted — success, empty, error, or skipped for want of a valid id — do not call again, even with a different id. Input/ordering details: see Conversation Transcript Rule below.
|
|
703
|
+
- Get_Activity_Details is NOT used in any deal-analysis intent — do not call it.
|
|
704
|
+
- Get_Conversation_Intelligence may be called up to 3x in `deal_summary` using 3 DIFFERENT call IDs; distinct ids are not repeats, so the latch does not block them.
|
|
705
|
+
|
|
706
|
+
## Conversation Intelligence Input Lock
|
|
707
|
+
- Get_Activities_Timeline MUST be called BEFORE Get_Conversation_Intelligence.
|
|
708
|
+
- From the timeline, extract ONLY activityIds starting with "0LQ" (voice) or "6qr" (video). Sort by recency (newest first).
|
|
709
|
+
- Each Get_Conversation_Intelligence invocation MUST include both VideoOrVoiceCallId (the 0LQ.../6qr... id) AND RelatedRecordId (the 006... Opportunity id).
|
|
710
|
+
- Counts:
|
|
711
|
+
- `deal_summary`: up to 3 invocations using the 3 most recent unique valid IDs (3 if ≥3 exist, 2 if 2 exist, 1 if 1 exists, 0 if none).
|
|
712
|
+
- `last_interaction`: 1 invocation using the most recent valid ID.
|
|
713
|
+
- `next_steps`: 1 invocation using the most relevant recent valid ID.
|
|
714
|
+
- `deal_risk`: do NOT call Get_Conversation_Intelligence; rely on Search_Conversations.
|
|
715
|
+
- Invalid invocations (missing VideoOrVoiceCallId, missing RelatedRecordId, ID not from timeline, ID not starting with "0LQ" or "6qr") DO NOT count toward required calls.
|
|
716
|
+
|
|
717
|
+
## Conversation Transcript Rule
|
|
718
|
+
- Get_Conversation_Transcript is GATED on the transcriptCallId variable: it is structurally uncallable until you call {!@actions.Set_TranscriptCallId} with transcriptCallId set to a valid "0LQ"/"6qr" call id, and its recordId is bound to that variable (you do NOT type recordId).
|
|
719
|
+
- SEQUENCE (deal_summary, last_interaction): after a valid Get_Conversation_Intelligence call returns, take the SAME call id you passed as its VideoOrVoiceCallId, call {!@actions.Set_TranscriptCallId} with transcriptCallId=that id, THEN call Get_Conversation_Transcript ONCE. Do this for the most recent valid CI-backed call only.
|
|
720
|
+
- `deal_risk`: do NOT call Get_Conversation_Transcript (and do NOT call Set_TranscriptCallId).
|
|
721
|
+
- If no valid CI-backed call id exists, do NOT call Set_TranscriptCallId; the transcript stays uncallable and is skipped.
|
|
722
|
+
|
|
723
|
+
## Account Resolution Rule
|
|
724
|
+
- Account plan retrieval is not available in this configuration. Do NOT call Get_Account_and_Account_Plan.
|
|
725
|
+
- For deal-analysis intents, resolve the parent Account ID from Get_Record_Details and call Set_Acc to set the account id and name. Use this for Set_Acc and web search context only.
|
|
726
|
+
|
|
727
|
+
## Get Email
|
|
728
|
+
- When calling {!@actions.Get_Related_Emails} use these default inputs startDate = today minus 30 days, endDate = today, emailCount = 25.
|
|
729
|
+
- Unless the user explicitly provides a different timeframe or email count.
|
|
730
|
+
|
|
731
|
+
## Identify Key Contacts Rule
|
|
732
|
+
- For `deal_summary`, MUST execute Identify_Key_Contacts with the current Opportunity ID. If the action requires Account context, use the Account ID derived from Get_Record_Details. If no key contacts are returned, call Query_Records with a plain-English query that names the object type and the parent Opportunity ID (e.g., query="Find all OpportunityContactRole records for Opportunity 006XXX..."). Do NOT pass SOQL or SQL. Do NOT fabricate stakeholders.
|
|
733
|
+
|
|
734
|
+
## CONVERSATION INTELLIGENCE EXECUTION
|
|
735
|
+
STEP 1 — Call Get_Activities_Timeline FIRST.
|
|
736
|
+
STEP 2 — From the timeline response, extract ONLY activityId values starting with "0LQ" (voice) or "6qr" (video). Ignore all other activity types.
|
|
737
|
+
STEP 3 — Sort the call IDs by recency (newest first; use timeline order).
|
|
738
|
+
STEP 4 — Build inputs as { "VideoOrVoiceCallId": "<0LQ... or 6qr...>", "RelatedRecordId": "<006...>" }.
|
|
739
|
+
STEP 5 — Execution counts per intent:
|
|
740
|
+
- `deal_summary`: call Get_Conversation_Intelligence exactly 3 times if ≥3 valid call IDs exist (top 3 unique most recent). With 2 IDs → 2 times. With 1 ID → 1 time. With 0 IDs → do NOT call.
|
|
741
|
+
- `last_interaction`: call once using the most recent ID.
|
|
742
|
+
- `next_steps`: call once using the most relevant recent ID.
|
|
743
|
+
- `deal_risk`: do not loop CI; rely on Search_Conversations.
|
|
744
|
+
STEP 6 — INVALID CALLS (do NOT count toward required calls): missing VideoOrVoiceCallId, missing RelatedRecordId, ID not from timeline, ID not starting with "0LQ" or "6qr".
|
|
745
|
+
|
|
746
|
+
## GLOBAL ACCOUNT PLAN INPUT RULE (HARD)
|
|
747
|
+
Account plan retrieval is not available in this configuration. Do NOT call Get_Account_and_Account_Plan under any circumstances.
|
|
748
|
+
|
|
749
|
+
## GLOBAL CONVERSATION TRANSCRIPT RULE (HARD)
|
|
750
|
+
- Get_Conversation_Transcript is only called AFTER a valid Get_Conversation_Intelligence call has been made.
|
|
751
|
+
- Get_Conversation_Transcript is gated on and supplied by the transcriptCallId variable (recordId is bound to it): call {!@actions.Set_TranscriptCallId} with transcriptCallId = the SAME "0LQ"/"6qr" call id used as VideoOrVoiceCallId in the corresponding Get_Conversation_Intelligence invocation, THEN call Get_Conversation_Transcript. Do not type recordId directly.
|
|
752
|
+
- `deal_summary`: if ≥1 valid CI-backed call IDs exist, call Get_Conversation_Transcript for the most recent valid call used in CI. Transcript does NOT replace WebSearch and does NOT replace any CI call.
|
|
753
|
+
- `last_interaction`: if a valid recent call ID exists, call Get_Conversation_Transcript for that call.
|
|
754
|
+
- `deal_risk`: do NOT call Get_Conversation_Transcript.
|
|
755
|
+
- If no valid call ID exists, do NOT call Get_Conversation_Transcript.
|
|
756
|
+
|
|
757
|
+
## GLOBAL EMAIL RULE (HARD)
|
|
758
|
+
Whenever the use case includes Get_Related_Emails:
|
|
759
|
+
- Default inputs: StartDate = 30 days before today, EndDate = today, EmailCount = 25.
|
|
760
|
+
- If the user explicitly provides a different timeframe or email count, use the user-provided values.
|
|
761
|
+
- Required executions MUST NOT be skipped; retry once on failure; treat empty/null/INTERNAL_ERROR/UNKNOWN_EXCEPTION as "no data found".
|
|
762
|
+
|
|
763
|
+
## GLOBAL GET ACTIVITY DETAILS INPUT RULE (HARD)
|
|
764
|
+
Whenever Get_Activity_Details is called:
|
|
765
|
+
- activityId MUST be the ID of a Call, Email, Task, or Event record. Valid ID prefixes: "0LQ" (voice call), "6qr" (video call), "00T" (task), "00U" (event), "02s" (email message).
|
|
766
|
+
- NEVER pass a 006... Opportunity ID, 001... Account ID, 003... Contact ID, or any other non-activity record ID as activityId.
|
|
767
|
+
- The activityId MUST come from the activityId field of a record returned by Get_Activities_Timeline. Do NOT derive it from mainRecordId, currentRecordId, or any other variable.
|
|
768
|
+
- If Get_Activities_Timeline has not yet been called or returned no activity IDs, do NOT call Get_Activity_Details.
|
|
769
|
+
- If no valid activity ID (starting with "0LQ", "6qr", "00T", "00U", or "02s") is available, skip Get_Activity_Details entirely.
|
|
770
|
+
- Calling Get_Activity_Details with a non-activity ID is an INVALID invocation and WILL result in an error.
|
|
771
|
+
|
|
772
|
+
## Slack Search Policy
|
|
773
|
+
- {!@actions.SearchSlack} is included in the batch for `pov_meeting_prep`, `account_plan`, and `conversation_insights`.
|
|
774
|
+
- Call {!@actions.SearchSlack} in the same parallel batch as all other intent actions.
|
|
775
|
+
- Use {!@variables.currentAccountName} as the query when set; otherwise fall back to {!@variables.currentRecordName}.
|
|
776
|
+
- If SearchSlack returns {}, [], null, INTERNAL_ERROR, UNKNOWN_EXCEPTION, or any HTTP error (400, 500, etc.): silently discard and continue. Slack errors MUST NOT stop execution or prevent other batch actions from completing.
|
|
777
|
+
- Include Slack results in the returned payload only if successful; otherwise omit silently.
|
|
778
|
+
|
|
779
|
+
## ID INDEX (MANDATORY — output ONCE at the very END of your response)
|
|
780
|
+
Populate the ID index with every record and its ID from ALL action outputs — including data pre-fetched by before_reasoning and cached in {!@variables.getRecordDetailsResult}, {!@variables.getActivitiesTimelineResult}, {!@variables.getRelatedNotesResult}, and {!@variables.getRelatedEmailResult}. Output this block ONCE at the very END of your response only. The caller uses this to build hyperlinks.
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
---ID-INDEX---
|
|
784
|
+
Account: <account name> | <accountRecord.id>
|
|
785
|
+
Lead: <lead name> | <leadRecord.id> (only when the resolved record is a Lead — id starts with "00Q"; use the Lead's Name and Id from Get_Record_Details)
|
|
786
|
+
Contact: <contact name> | <contact.id> (most recent 3 only; one line per contact)
|
|
787
|
+
Activity: <subject or title> | <activity.activityId> (most recent 3 only; one line per activity)
|
|
788
|
+
Note: <title> | <note.id> (most recent 3 only; one line per note)
|
|
789
|
+
Email: <subject> | <email.id> (most recent 3 only; one line per email)
|
|
790
|
+
Case: <subject> | <case.id> (most recent 3 only; one line per case)
|
|
791
|
+
---END-ID-INDEX---
|
|
792
|
+
|
|
793
|
+
Rules:
|
|
794
|
+
- Include ONLY entries where the ID is non-null and non-empty.
|
|
795
|
+
- Omit the entire section only if NO IDs are available at all.
|
|
796
|
+
- Do NOT modify or summarize record names — use exact names from action outputs.
|
|
797
|
+
- This section is for the caller's internal use only; it will not be shown to the user.
|
|
798
|
+
|
|
799
|
+
# Strict Limitations
|
|
800
|
+
- Return all data to caller — no synthesis
|
|
801
|
+
- Never re-call Identify_Record_by_Name — it has already been called automatically by before_reasoning
|
|
802
|
+
- CRITICAL: Never call the intent actions one by one sequentially. Always batch them into a single parallel turn.
|
|
803
|
+
actions:
|
|
804
|
+
Set_Acc: @utils.setVariables
|
|
805
|
+
with currentAccountId=...
|
|
806
|
+
with currentAccountName=...
|
|
807
|
+
Set_MainRecordId: @utils.setVariables
|
|
808
|
+
with mainRecordId=...
|
|
809
|
+
Set_TranscriptCallId: @utils.setVariables
|
|
810
|
+
with transcriptCallId=...
|
|
811
|
+
Get_Record_Details: @actions.Get_Record_Details
|
|
812
|
+
available when @variables.mainRecordId is not None and @variables.mainRecordId != ""
|
|
813
|
+
with recordId=@variables.mainRecordId
|
|
814
|
+
Get_Activities_Timeline: @actions.Get_Activities_Timeline
|
|
815
|
+
available when @variables.mainRecordId is not None and @variables.mainRecordId != ""
|
|
816
|
+
with recordId=@variables.mainRecordId
|
|
817
|
+
with activityTypes=None
|
|
818
|
+
with fromDateTime=None
|
|
819
|
+
with toDateTime=None
|
|
820
|
+
Get_Activity_Details: @actions.Get_Activity_Details
|
|
821
|
+
available when @variables.currentIntentCode == "conversation_insights"
|
|
822
|
+
with activityId=...
|
|
823
|
+
Get_Related_Notes: @actions.Get_Related_Notes
|
|
824
|
+
available when @variables.mainRecordId is not None and @variables.mainRecordId != ""
|
|
825
|
+
with recordId=@variables.mainRecordId
|
|
826
|
+
Get_Related_Emails: @actions.Get_Related_Emails
|
|
827
|
+
available when @variables.mainRecordId is not None and @variables.mainRecordId != ""
|
|
828
|
+
with recordId=@variables.mainRecordId
|
|
829
|
+
with startDate=...
|
|
830
|
+
with endDate=...
|
|
831
|
+
with emailCount=...
|
|
832
|
+
Get_Conversation_Intelligence: @actions.Get_Conversation_Intelligence
|
|
833
|
+
available when @variables.mainRecordId is not None and @variables.mainRecordId != "" and @variables.currentObjectType != "Event"
|
|
834
|
+
with RelatedRecordId=@variables.mainRecordId
|
|
835
|
+
with VideoOrVoiceCallId=...
|
|
836
|
+
Get_Conversation_Transcript: @actions.Get_Conversation_Transcript
|
|
837
|
+
available when @variables.currentObjectType != "Event" and @variables.transcriptCallId is not None and @variables.transcriptCallId != ""
|
|
838
|
+
with recordId=@variables.transcriptCallId
|
|
839
|
+
Search_Conversations: @actions.Search_Conversations
|
|
840
|
+
available when @variables.mainRecordId is not None and @variables.mainRecordId != ""
|
|
841
|
+
with relatedRecordId=@variables.mainRecordId
|
|
842
|
+
with searchQuery=...
|
|
843
|
+
with startDate=None
|
|
844
|
+
with endDate=None
|
|
845
|
+
Identify_Key_Contacts: @actions.Identify_Key_Contacts
|
|
846
|
+
available when @variables.mainRecordId is not None and @variables.mainRecordId != ""
|
|
847
|
+
with recordId=@variables.mainRecordId
|
|
848
|
+
with userInput=...
|
|
849
|
+
with actionName="Identify Key Contacts"
|
|
850
|
+
Query_Records: @actions.Query_Records
|
|
851
|
+
with query=...
|
|
852
|
+
with customizationInstructions=...
|
|
853
|
+
SearchSlack: @actions.SearchSlack
|
|
854
|
+
description: "Search Slack for context relevant to the current account or record. Call in parallel with other retrieval actions. Fail gracefully if Slack is unavailable."
|
|
855
|
+
available when @variables.currentRecordName != "" or @variables.currentAccountName != ""
|
|
856
|
+
with query=@variables.currentAccountName or @variables.currentRecordName
|
|
857
|
+
actions:
|
|
858
|
+
Identify_Record_by_Name:
|
|
859
|
+
description: "Searches for Salesforce CRM records by name and returns a list of matching Salesforce CRM record IDs."
|
|
860
|
+
label: "Identify Record by Name"
|
|
861
|
+
require_user_confirmation: False
|
|
862
|
+
include_in_progress_indicator: True
|
|
863
|
+
source: "EmployeeCopilot__IdentifyRecordByName"
|
|
864
|
+
target: "standardInvocableAction://identifyRecordByName"
|
|
865
|
+
inputs:
|
|
866
|
+
"recordName": string
|
|
867
|
+
description: "The name or title of the Salesforce record extracted from user input. The name can be used to find a Salesforce CRM record."
|
|
868
|
+
label: "recordName"
|
|
869
|
+
is_required: True
|
|
870
|
+
is_user_input: True
|
|
871
|
+
"objectApiName": string
|
|
872
|
+
description: "The API name of the Salesforce object (such as Account or Opportunity) associated with the record the user wants to find. The name can be obtained from the context, like with [object_type] if present and does not always require the identifyObjectByName action. (eg. IdentifyRecordByName(recordName=\"Acme\", objectApiName=\"Account\")). Only if the object name can't be identified, leave this empty."
|
|
873
|
+
label: "objectApiName"
|
|
874
|
+
is_required: False
|
|
875
|
+
is_user_input: False
|
|
876
|
+
outputs:
|
|
877
|
+
"searchResults": list[object]
|
|
878
|
+
description: "A list of the matching Salesforce record ids in descending order of relevance."
|
|
879
|
+
label: "searchResults"
|
|
880
|
+
is_displayable: False
|
|
881
|
+
filter_from_agent: False
|
|
882
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
883
|
+
Get_Record_Details:
|
|
884
|
+
description: "Get details for a Salesforce CRM record by ID or fetch related records. For ID search: pass the record ID directly (always returns exactly 1 result or null if not found). For related records: pass the parent record ID to retrieve related records. Format results as: Query: Name/ID matching '[ID]' (for ID search) or Query: Related objects to Name/ID '[ID]' (for related records)\nResult:\nID1, ObjectType1, Name1\nID2, ObjectType2, Name2"
|
|
885
|
+
label: "Get Record Details"
|
|
886
|
+
require_user_confirmation: False
|
|
887
|
+
include_in_progress_indicator: True
|
|
888
|
+
progress_indicator_message: "Getting details"
|
|
889
|
+
source: "EmployeeCopilot__GetRecordDetails"
|
|
890
|
+
target: "standardInvocableAction://getDataForGrounding"
|
|
891
|
+
inputs:
|
|
892
|
+
"recordId": object
|
|
893
|
+
description: "The ID of a Salesforce record to get the record details for."
|
|
894
|
+
label: "Record ID"
|
|
895
|
+
is_required: True
|
|
896
|
+
is_user_input: False
|
|
897
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
898
|
+
outputs:
|
|
899
|
+
"snapshot": string
|
|
900
|
+
description: "Record details, including object fields and values, records from related lists, and more."
|
|
901
|
+
label: "Record Details"
|
|
902
|
+
is_displayable: False
|
|
903
|
+
filter_from_agent: False
|
|
904
|
+
Get_Activities_Timeline:
|
|
905
|
+
description: "Retrieves a timeline of activities (Calls, Emails, Events, Tasks) for a specific record (Account, Opportunity, Contact, Lead, etc.)."
|
|
906
|
+
label: "Get Activities Timeline"
|
|
907
|
+
require_user_confirmation: False
|
|
908
|
+
include_in_progress_indicator: True
|
|
909
|
+
progress_indicator_message: "Retrieving activities timeline"
|
|
910
|
+
source: "EmployeeCopilot__GetActivitiesTimeline"
|
|
911
|
+
target: "standardInvocableAction://getActivitiesTimeline"
|
|
912
|
+
inputs:
|
|
913
|
+
"recordId": object
|
|
914
|
+
description: "The Salesforce record ID for which activities are being fetched."
|
|
915
|
+
label: "Record ID"
|
|
916
|
+
is_required: True
|
|
917
|
+
is_user_input: False
|
|
918
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
919
|
+
"activityTypes": list[object]
|
|
920
|
+
description: "A list of activity types used to retrieve the timeline. For example, Call, Email, Task, and Event."
|
|
921
|
+
label: "Activity Types"
|
|
922
|
+
is_required: False
|
|
923
|
+
is_user_input: False
|
|
924
|
+
complex_data_type_name: "lightning__textType"
|
|
925
|
+
"fromDateTime": string
|
|
926
|
+
description: "The start date and time in the format yyyy-MM-ddTHH:mm. Timezone information isn't included."
|
|
927
|
+
label: "From Date Time"
|
|
928
|
+
is_required: False
|
|
929
|
+
is_user_input: False
|
|
930
|
+
"toDateTime": string
|
|
931
|
+
description: "The end date and time in the format yyyy-MM-ddTHH:mm. Timezone information isn't included."
|
|
932
|
+
label: "To Date Time"
|
|
933
|
+
is_required: False
|
|
934
|
+
is_user_input: False
|
|
935
|
+
outputs:
|
|
936
|
+
"activitiesList": string
|
|
937
|
+
description: "A list of activities related to the record ID, including high-level details."
|
|
938
|
+
label: "Activities List"
|
|
939
|
+
is_displayable: False
|
|
940
|
+
filter_from_agent: False
|
|
941
|
+
Get_Activity_Details:
|
|
942
|
+
description: "Provides data about an activity record, including its content and other key details. The record type is strictly limited to a Call, Email, Event, or Task."
|
|
943
|
+
label: "Get Activity Details"
|
|
944
|
+
require_user_confirmation: False
|
|
945
|
+
include_in_progress_indicator: True
|
|
946
|
+
progress_indicator_message: "Retrieving activity details"
|
|
947
|
+
source: "EmployeeCopilot__GetActivityDetails"
|
|
948
|
+
target: "standardInvocableAction://getActivityDetails"
|
|
949
|
+
inputs:
|
|
950
|
+
activityId: object
|
|
951
|
+
description: "The record ID of a Call, Email, Task, or Event."
|
|
952
|
+
label: "Activity ID"
|
|
953
|
+
is_required: True
|
|
954
|
+
is_user_input: False
|
|
955
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
956
|
+
outputs:
|
|
957
|
+
"activityDetails": string
|
|
958
|
+
description: "The data for the activity record, including its content and other relevant details."
|
|
959
|
+
label: "Activity Details"
|
|
960
|
+
is_displayable: False
|
|
961
|
+
filter_from_agent: False
|
|
962
|
+
Get_Related_Notes:
|
|
963
|
+
description: "Retrieves notes related to a record (Account, Opportunity, Contact, Lead, etc.)."
|
|
964
|
+
label: "Get Related Notes"
|
|
965
|
+
require_user_confirmation: False
|
|
966
|
+
include_in_progress_indicator: True
|
|
967
|
+
progress_indicator_message: "Getting notes"
|
|
968
|
+
source: "SalesMgmt__GetRelatedNotes"
|
|
969
|
+
target: "flow://SalesMgmt__GetRelatedContentNotes"
|
|
970
|
+
inputs:
|
|
971
|
+
"recordId": string
|
|
972
|
+
description: "The unique ID for the parent record from which notes will be retrieved."
|
|
973
|
+
label: "Record ID"
|
|
974
|
+
is_required: True
|
|
975
|
+
is_user_input: True
|
|
976
|
+
outputs:
|
|
977
|
+
"collectionOfNotes": list[object]
|
|
978
|
+
description: "The list of latest notes."
|
|
979
|
+
label: "Notes"
|
|
980
|
+
is_displayable: False
|
|
981
|
+
filter_from_agent: False
|
|
982
|
+
complex_data_type_name: "lightning__TextType"
|
|
983
|
+
Get_Related_Emails:
|
|
984
|
+
description: "Retrieves emails related to a record (Account, Opportunity, Contact, Lead, etc.)."
|
|
985
|
+
label: "Get Related Emails"
|
|
986
|
+
require_user_confirmation: False
|
|
987
|
+
include_in_progress_indicator: True
|
|
988
|
+
progress_indicator_message: "Retrieving related emails"
|
|
989
|
+
source: "SalesMgmt__GetRelatedEmails"
|
|
990
|
+
target: "standardInvocableAction://getRelatedEmails"
|
|
991
|
+
inputs:
|
|
992
|
+
"recordId": string
|
|
993
|
+
description: "The unique ID of the parent record from which the email will be retrieved."
|
|
994
|
+
label: "Record ID"
|
|
995
|
+
is_required: True
|
|
996
|
+
is_user_input: True
|
|
997
|
+
"emailCount": number
|
|
998
|
+
description: "The number of records retrieved and stored in the emailCollection variable."
|
|
999
|
+
label: "Email Count"
|
|
1000
|
+
is_required: True
|
|
1001
|
+
is_user_input: True
|
|
1002
|
+
complex_data_type_name: "lightning__numberType"
|
|
1003
|
+
"startDate": date
|
|
1004
|
+
description: "Use the date 29 days before today in YYYY-MM-DD format."
|
|
1005
|
+
label: "Start Date"
|
|
1006
|
+
is_required: True
|
|
1007
|
+
is_user_input: True
|
|
1008
|
+
complex_data_type_name: "lightning__dateType"
|
|
1009
|
+
"endDate": date
|
|
1010
|
+
description: "Use today's date in YYYY-MM-DD format."
|
|
1011
|
+
label: "End Date"
|
|
1012
|
+
is_required: True
|
|
1013
|
+
is_user_input: True
|
|
1014
|
+
complex_data_type_name: "lightning__dateType"
|
|
1015
|
+
outputs:
|
|
1016
|
+
"emailCollection": list[object]
|
|
1017
|
+
description: "The list of latest emails, grouped by email subject line."
|
|
1018
|
+
label: "Emails"
|
|
1019
|
+
is_displayable: False
|
|
1020
|
+
filter_from_agent: False
|
|
1021
|
+
complex_data_type_name: "lightning__TextType"
|
|
1022
|
+
Get_Conversation_Intelligence:
|
|
1023
|
+
description: "Retrieves conversation intelligence for a record."
|
|
1024
|
+
label: "Get Conversation Intelligence"
|
|
1025
|
+
require_user_confirmation: False
|
|
1026
|
+
include_in_progress_indicator: True
|
|
1027
|
+
progress_indicator_message: "Retrieving conversation intelligence"
|
|
1028
|
+
target: "flow://SalesMgmt__GetConversationIntelligence"
|
|
1029
|
+
source: "SalesMgmt__GetConversationIntelligence"
|
|
1030
|
+
inputs:
|
|
1031
|
+
"RelatedRecordId": string
|
|
1032
|
+
description: "The unique ID of the parent record that contains the video or voice call data from which the conversation insights will be retrieved."
|
|
1033
|
+
label: "Record ID"
|
|
1034
|
+
is_required: True
|
|
1035
|
+
is_user_input: True
|
|
1036
|
+
"VideoOrVoiceCallId": string
|
|
1037
|
+
description: "Video Call Id or Voice Call Id to get conversation intelligence for."
|
|
1038
|
+
label: "Video or Voice Call ID"
|
|
1039
|
+
is_required: False
|
|
1040
|
+
is_user_input: False
|
|
1041
|
+
outputs:
|
|
1042
|
+
"ConversationMomentInsights": list[object]
|
|
1043
|
+
description: "A list of Apex objects that contain insights from specific conversations during video or voice calls, such as pricing discussions or mentions of keywords about products or competitors. If no insights are found, the list remains empty."
|
|
1044
|
+
label: "Conversation Moment Insights"
|
|
1045
|
+
is_displayable: False
|
|
1046
|
+
filter_from_agent: False
|
|
1047
|
+
complex_data_type_name: "@apexClassType/ConnectApi__ConversationMomentInsight"
|
|
1048
|
+
"ConversationInsights": list[object]
|
|
1049
|
+
description: "A list of Apex objects that contain insights related to the overall content of video or voice calls, such as generative conversation insights."
|
|
1050
|
+
label: "Conversation Insights"
|
|
1051
|
+
is_displayable: False
|
|
1052
|
+
filter_from_agent: False
|
|
1053
|
+
complex_data_type_name: "@apexClassType/ConnectApi__ConversationInsight"
|
|
1054
|
+
"ConversationIntelligence": object
|
|
1055
|
+
description: "Conversation Intelligence"
|
|
1056
|
+
label: "Conversation Intelligence"
|
|
1057
|
+
is_displayable: False
|
|
1058
|
+
filter_from_agent: False
|
|
1059
|
+
complex_data_type_name: "@apexClassType/ConnectApi__ConversationIntelligence"
|
|
1060
|
+
"LatestVideoOrVoiceCallRecordId": string
|
|
1061
|
+
description: "The record ID for the video or voice call related to the conversation."
|
|
1062
|
+
label: "Video or Voice Call ID"
|
|
1063
|
+
is_displayable: False
|
|
1064
|
+
filter_from_agent: False
|
|
1065
|
+
"LatestVideoOrVoiceCallCreatedDate": date
|
|
1066
|
+
description: "The date that the video or voice call related to the conversation was created."
|
|
1067
|
+
label: "Video or Voice Call Created Date"
|
|
1068
|
+
is_displayable: False
|
|
1069
|
+
filter_from_agent: False
|
|
1070
|
+
complex_data_type_name: "lightning__dateType"
|
|
1071
|
+
AnswerQuestionsWithKnowledge:
|
|
1072
|
+
description: "Answers questions about company policies and procedures, troubleshooting steps, or product information. For example: “What is your return policy?” “How do I fix an issue?” or “What features does a product have?”"
|
|
1073
|
+
label: "Answer Questions with Knowledge"
|
|
1074
|
+
require_user_confirmation: False
|
|
1075
|
+
include_in_progress_indicator: True
|
|
1076
|
+
progress_indicator_message: "Getting answers"
|
|
1077
|
+
source: "EmployeeCopilot__AnswerQuestionsWithKnowledge"
|
|
1078
|
+
target: "standardInvocableAction://streamKnowledgeSearch"
|
|
1079
|
+
inputs:
|
|
1080
|
+
"query": string
|
|
1081
|
+
description: "Required. A string created by generative AI to be used in the knowledge article search."
|
|
1082
|
+
label: "Query"
|
|
1083
|
+
is_required: True
|
|
1084
|
+
is_user_input: True
|
|
1085
|
+
"citationsUrl": string = @knowledge.citations_url
|
|
1086
|
+
description: "The URL to use for citations for custom Agents."
|
|
1087
|
+
label: "Citations Url"
|
|
1088
|
+
is_required: False
|
|
1089
|
+
is_user_input: True
|
|
1090
|
+
"ragFeatureConfigId": string = @knowledge.rag_feature_config_id
|
|
1091
|
+
description: "The RAG Feature ID to use for grounding this copilot action invocation."
|
|
1092
|
+
label: "RAG Feature Configuration Id"
|
|
1093
|
+
is_required: False
|
|
1094
|
+
is_user_input: True
|
|
1095
|
+
"citationsEnabled": boolean = @knowledge.citations_enabled
|
|
1096
|
+
description: "Whether or not citations are enabled."
|
|
1097
|
+
label: "Citations Enabled"
|
|
1098
|
+
is_required: False
|
|
1099
|
+
is_user_input: True
|
|
1100
|
+
outputs:
|
|
1101
|
+
"knowledgeSummary": object
|
|
1102
|
+
description: "A string formatted as rich text that includes a summary of the information retrieved from the knowledge articles and citations to those articles."
|
|
1103
|
+
label: "Knowledge Summary"
|
|
1104
|
+
is_displayable: True
|
|
1105
|
+
filter_from_agent: False
|
|
1106
|
+
complex_data_type_name: "lightning__richTextType"
|
|
1107
|
+
"citationSources": object
|
|
1108
|
+
description: "Source links for the chunks in the hydrated prompt that's used by the planner service."
|
|
1109
|
+
label: "Citation Sources"
|
|
1110
|
+
is_displayable: False
|
|
1111
|
+
filter_from_agent: False
|
|
1112
|
+
complex_data_type_name: "@apexClassType/AiCopilot__GenAiCitationInput"
|
|
1113
|
+
Get_Conversation_Transcript:
|
|
1114
|
+
description: "Gets the conversation transcript for a single voice or video call. Call only after Get_Conversation_Intelligence has been executed for the same call; pass the Voice Call Id or Video Call Id (activityId starting with '0LQ' or '6qr') as recordId."
|
|
1115
|
+
label: "Get Conversation Transcript"
|
|
1116
|
+
require_user_confirmation: False
|
|
1117
|
+
include_in_progress_indicator: True
|
|
1118
|
+
progress_indicator_message: "Getting conversations"
|
|
1119
|
+
source: "SalesMgmt__GetConversationTranscript"
|
|
1120
|
+
target: "standardInvocableAction://getConversationTranscript"
|
|
1121
|
+
inputs:
|
|
1122
|
+
"recordId": string
|
|
1123
|
+
description: "The Voice Call Id or Video Call Id (activityId starting with '0LQ' for voice or '6qr' for video) to retrieve the transcript for. Must come from the same call used for Get_Conversation_Intelligence."
|
|
1124
|
+
label: "Record ID"
|
|
1125
|
+
is_required: True
|
|
1126
|
+
is_user_input: True
|
|
1127
|
+
outputs:
|
|
1128
|
+
"transcript": object
|
|
1129
|
+
description: "The conversation transcript for the specified voice or video call."
|
|
1130
|
+
label: "Transcript"
|
|
1131
|
+
is_displayable: False
|
|
1132
|
+
filter_from_agent: False
|
|
1133
|
+
complex_data_type_name: "@apexClassType/ConnectApi__ConversationTranscript"
|
|
1134
|
+
Search_Conversations:
|
|
1135
|
+
description: "Searches voice and video call transcripts for specific topics or keywords related to a record. Use for Deal Risk to surface blockers, objections, and risk signals across multiple conversations without retrieving full transcripts."
|
|
1136
|
+
label: "Search Conversations"
|
|
1137
|
+
require_user_confirmation: False
|
|
1138
|
+
include_in_progress_indicator: True
|
|
1139
|
+
progress_indicator_message: "Searching conversations"
|
|
1140
|
+
source: "SalesMgmt__SearchConversations"
|
|
1141
|
+
target: "standardInvocableAction://searchConversations"
|
|
1142
|
+
inputs:
|
|
1143
|
+
"relatedRecordId": string
|
|
1144
|
+
description: "The unique ID of the parent record (typically the 006... Opportunity ID) whose related conversations should be searched."
|
|
1145
|
+
label: "Related Record ID"
|
|
1146
|
+
is_required: True
|
|
1147
|
+
is_user_input: False
|
|
1148
|
+
"searchQuery": string
|
|
1149
|
+
description: "Free-text query describing the topic, risk, blocker, or theme to search for across conversation transcripts."
|
|
1150
|
+
label: "Search Query"
|
|
1151
|
+
is_required: True
|
|
1152
|
+
is_user_input: False
|
|
1153
|
+
"startDate": date
|
|
1154
|
+
description: "Optional start of the date range to search. If omitted, searches the default recent window."
|
|
1155
|
+
label: "Start Date"
|
|
1156
|
+
is_required: False
|
|
1157
|
+
is_user_input: False
|
|
1158
|
+
complex_data_type_name: "lightning__dateType"
|
|
1159
|
+
"endDate": date
|
|
1160
|
+
description: "Optional end of the date range to search. If omitted, searches up to today."
|
|
1161
|
+
label: "End Date"
|
|
1162
|
+
is_required: False
|
|
1163
|
+
is_user_input: False
|
|
1164
|
+
complex_data_type_name: "lightning__dateType"
|
|
1165
|
+
outputs:
|
|
1166
|
+
"searchResults": list[object]
|
|
1167
|
+
description: "A list of conversation search results matching the query, each tied back to its originating call activity."
|
|
1168
|
+
label: "Search Results"
|
|
1169
|
+
is_displayable: False
|
|
1170
|
+
filter_from_agent: False
|
|
1171
|
+
complex_data_type_name: "@apexClassType/ConnectApi__ConversationSearchResult"
|
|
1172
|
+
Identify_Key_Contacts:
|
|
1173
|
+
description: "Identifies key contacts for an Opportunity or Account — people who can influence, obstruct, impede, decide, or are involved in the final outcome of a deal. The primary contact is also a key contact. Identify_Record_by_Name MUST have been called prior to obtain the Opportunity or Account ID."
|
|
1174
|
+
label: "Identify Key Contacts"
|
|
1175
|
+
require_user_confirmation: False
|
|
1176
|
+
include_in_progress_indicator: True
|
|
1177
|
+
progress_indicator_message: "Finding key contacts"
|
|
1178
|
+
source: "Sales__IdentifyKeyContacts"
|
|
1179
|
+
target: "standardInvocableAction://reviewBuyingCommittee"
|
|
1180
|
+
inputs:
|
|
1181
|
+
"recordId": object
|
|
1182
|
+
description: "The Opportunity ID (006...) or Account ID (001...) whose key contacts should be identified. Prefer the current Opportunity ID; if the action requires Account context, pass the Account ID derived from Get_Record_Details."
|
|
1183
|
+
label: "Record ID"
|
|
1184
|
+
is_required: True
|
|
1185
|
+
is_user_input: False
|
|
1186
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
1187
|
+
"userInput": string
|
|
1188
|
+
description: "The question or request from the user's input that motivates this lookup (e.g., the verbatim utterance about the deal, brief, or stakeholders)."
|
|
1189
|
+
label: "User Input"
|
|
1190
|
+
is_required: True
|
|
1191
|
+
is_user_input: True
|
|
1192
|
+
"actionName": string
|
|
1193
|
+
description: "Constant value 'Identify Key Contacts'. This input is a constant variable used only for this action; do not vary the value."
|
|
1194
|
+
label: "Action Name"
|
|
1195
|
+
is_required: False
|
|
1196
|
+
is_user_input: False
|
|
1197
|
+
outputs:
|
|
1198
|
+
"contacts": list[object]
|
|
1199
|
+
description: "The key contacts of the Opportunity or Account."
|
|
1200
|
+
label: "Contacts"
|
|
1201
|
+
is_displayable: True
|
|
1202
|
+
filter_from_agent: False
|
|
1203
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
1204
|
+
Query_Records:
|
|
1205
|
+
description: "Finds and retrieves Salesforce CRM records based on a natural-language request and specific conditions, such as field values. This action automatically identifies the correct records and object type. The query input MUST be plain English (do NOT generate SOQL or SQL) and MUST mention the Salesforce object type (e.g., 'Find all OpportunityContactRole records for Opportunity 006...')."
|
|
1206
|
+
label: "Query Records"
|
|
1207
|
+
require_user_confirmation: False
|
|
1208
|
+
include_in_progress_indicator: True
|
|
1209
|
+
progress_indicator_message: "Querying records"
|
|
1210
|
+
source: "EmployeeCopilot__QueryRecords"
|
|
1211
|
+
target: "standardInvocableAction://queryRecords"
|
|
1212
|
+
inputs:
|
|
1213
|
+
"query": string
|
|
1214
|
+
description: "The question or request in plain English. MUST mention the Salesforce object type (Account, Lead, Contact, OpportunityContactRole, etc.). Do NOT include SOQL or SQL. Example: 'Find all OpportunityContactRole records for Opportunity 006XXXX'."
|
|
1215
|
+
label: "Query"
|
|
1216
|
+
is_required: True
|
|
1217
|
+
is_user_input: True
|
|
1218
|
+
"customizationInstructions": string
|
|
1219
|
+
description: "Optional custom instructions to optimize the query (e.g., specific fields to include, sort order)."
|
|
1220
|
+
label: "Customization Instructions"
|
|
1221
|
+
is_required: False
|
|
1222
|
+
is_user_input: False
|
|
1223
|
+
outputs:
|
|
1224
|
+
"result": list[object]
|
|
1225
|
+
description: "A list of matching Salesforce CRM records with details about each record."
|
|
1226
|
+
label: "Result"
|
|
1227
|
+
is_displayable: False
|
|
1228
|
+
filter_from_agent: False
|
|
1229
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
1230
|
+
SearchSlack:
|
|
1231
|
+
description: "Searches Slack for messages, files, or enterprise content related to a record or account. Used to enrich research responses with internal Slack context. Mandatory for pov_meeting_prep, conversation_insights, and account_plan intents. Call in parallel with other retrieval actions. Fail gracefully if Slack is unavailable."
|
|
1232
|
+
label: "Search Slack"
|
|
1233
|
+
require_user_confirmation: False
|
|
1234
|
+
include_in_progress_indicator: True
|
|
1235
|
+
progress_indicator_message: "Searching Slack"
|
|
1236
|
+
source: "Slack__SearchSlack"
|
|
1237
|
+
target: "standardInvocableAction://slackAgentSearch"
|
|
1238
|
+
inputs:
|
|
1239
|
+
"query": string
|
|
1240
|
+
description: "The account or record name to search for in Slack. Use the current account name or record name as the query."
|
|
1241
|
+
label: "Query"
|
|
1242
|
+
is_required: True
|
|
1243
|
+
is_user_input: True
|
|
1244
|
+
"excludeChannels": string
|
|
1245
|
+
description: "Comma-separated list of channel IDs to exclude from search"
|
|
1246
|
+
label: "Exclude Channels"
|
|
1247
|
+
is_required: False
|
|
1248
|
+
is_user_input: False
|
|
1249
|
+
"includeChannels": string
|
|
1250
|
+
description: "Comma-separated list of channel IDs to include in search"
|
|
1251
|
+
label: "Include Channels"
|
|
1252
|
+
is_required: False
|
|
1253
|
+
is_user_input: False
|
|
1254
|
+
"channelTypes": string
|
|
1255
|
+
description: "Types of channels to search (e.g., public, private)"
|
|
1256
|
+
label: "Channel Types"
|
|
1257
|
+
is_required: False
|
|
1258
|
+
is_user_input: False
|
|
1259
|
+
"contentTypes": string
|
|
1260
|
+
description: "Types of content to search for (e.g., messages, files)"
|
|
1261
|
+
label: "Content Types"
|
|
1262
|
+
is_required: False
|
|
1263
|
+
is_user_input: False
|
|
1264
|
+
"contextChannelId": string
|
|
1265
|
+
description: "Channel ID for context-specific search"
|
|
1266
|
+
label: "Context Channel ID"
|
|
1267
|
+
is_required: False
|
|
1268
|
+
is_user_input: False
|
|
1269
|
+
outputs:
|
|
1270
|
+
"results": string
|
|
1271
|
+
description: "Search results containing messages, files, or enterprise content"
|
|
1272
|
+
label: "Search Results"
|
|
1273
|
+
is_displayable: False
|
|
1274
|
+
filter_from_agent: False
|
|
1275
|
+
|
|
1276
|
+
subagent Web_Retriever:
|
|
1277
|
+
description: "Utility subagent that performs web search and retrieves cached record research for records. Supports both CRM Flow (with recordId) and Direct Web Flow (without CRM lookup)."
|
|
1278
|
+
reasoning:
|
|
1279
|
+
instructions: ->
|
|
1280
|
+
| You are the Web Retriever - a utility subagent that performs web search and retrieves cached record research.
|
|
1281
|
+
|
|
1282
|
+
# EXECUTION ORDER — follow exactly, do not skip steps
|
|
1283
|
+
|
|
1284
|
+
## STEP 1: Get_Record_Research (MANDATORY FIRST — always attempt this before any web search)
|
|
1285
|
+
{!@actions.Get_Record_Research} is available whenever mainRecordId is set.
|
|
1286
|
+
Call {!@actions.Get_Record_Research} FIRST. Do NOT call any web search action before this.
|
|
1287
|
+
- If aiResearchPromptResultRecords is non-empty → return those results to the caller. STOP. Do NOT call any web search.
|
|
1288
|
+
- If aiResearchPromptResultRecords is empty or null → proceed to STEP 2.
|
|
1289
|
+
|
|
1290
|
+
## STEP 2: Web Search Fallback (only reached when STEP 1 returned no data)
|
|
1291
|
+
Get_Record_Research returned no cached results. Now run web searches:
|
|
1292
|
+
- If currentIntentCode is `pov_meeting_prep` → call {!@actions.Web_Search_Overview} AND {!@actions.Web_Search_News} IN PARALLEL. Do NOT call generic Web_Search.
|
|
1293
|
+
- Otherwise → decide on searches based on the user utterance and call them in parallel.
|
|
1294
|
+
|
|
1295
|
+
Return results to Research Specialist in clear, organized format with clickable hyperlinks.
|
|
1296
|
+
|
|
1297
|
+
# Strict Limitations
|
|
1298
|
+
|
|
1299
|
+
- ONLY web search and cached record research - no domain logic, no reasoning, no synthesis.
|
|
1300
|
+
- DO NOT resolve record names - CRM_Record_Retriever handles that
|
|
1301
|
+
- Pure utility - deterministic operations only. Control returns immediately to the caller after returning results.
|
|
1302
|
+
- CRITICAL: Get_Record_Research MUST be called first. Never call web search actions without first attempting Get_Record_Research.
|
|
1303
|
+
actions:
|
|
1304
|
+
Web_Search: @actions.Web_Search
|
|
1305
|
+
available when @variables.currentIntentCode != "pov_meeting_prep"
|
|
1306
|
+
with searchQuery=...
|
|
1307
|
+
with searchProvider="You.com"
|
|
1308
|
+
with siteFilter=None
|
|
1309
|
+
Web_Search_Overview: @actions.Web_Search
|
|
1310
|
+
available when @variables.currentIntentCode == "pov_meeting_prep" and @variables.currentAccountName != ""
|
|
1311
|
+
with searchQuery = @variables.currentAccountName + " company overview"
|
|
1312
|
+
with searchProvider="You.com"
|
|
1313
|
+
with siteFilter=None
|
|
1314
|
+
Web_Search_News: @actions.Web_Search
|
|
1315
|
+
available when @variables.currentIntentCode == "pov_meeting_prep" and @variables.currentAccountName != ""
|
|
1316
|
+
with searchQuery = @variables.currentAccountName + " news"
|
|
1317
|
+
with searchProvider="You.com"
|
|
1318
|
+
with siteFilter=None
|
|
1319
|
+
Get_Record_Research: @actions.Get_Record_Research
|
|
1320
|
+
available when @variables.currentAccountId is not None and @variables.currentAccountId != ""
|
|
1321
|
+
with recordId=@variables.currentAccountId
|
|
1322
|
+
actions:
|
|
1323
|
+
Web_Search:
|
|
1324
|
+
description: "Retrieves real-time or external information that isn't available in Salesforce CRM. Use it for questions about news, market trends, competitor details, and product comparisons, or when Salesforce answers need added context from the web."
|
|
1325
|
+
label: "Search The Web"
|
|
1326
|
+
require_user_confirmation: False
|
|
1327
|
+
include_in_progress_indicator: True
|
|
1328
|
+
progress_indicator_message: "Searching the web"
|
|
1329
|
+
source: "EmployeeCopilot__WebSearch"
|
|
1330
|
+
target: "standardInvocableAction://webSearchStream"
|
|
1331
|
+
inputs:
|
|
1332
|
+
"searchQuery": string
|
|
1333
|
+
description: "Free-text input that specifies what the user wants to search for."
|
|
1334
|
+
label: "Search Query"
|
|
1335
|
+
is_required: True
|
|
1336
|
+
is_user_input: True
|
|
1337
|
+
"searchProvider": string
|
|
1338
|
+
description: "The search provider used for web searches."
|
|
1339
|
+
label: "Search Provider"
|
|
1340
|
+
is_required: False
|
|
1341
|
+
is_user_input: False
|
|
1342
|
+
"siteFilter": string
|
|
1343
|
+
description: "A comma-separated list of up to 10 allowed domains to filter web search results."
|
|
1344
|
+
label: "Allowed Domains"
|
|
1345
|
+
is_required: False
|
|
1346
|
+
is_user_input: False
|
|
1347
|
+
outputs:
|
|
1348
|
+
"webSearchSummary": object
|
|
1349
|
+
description: "A prompt that summarizes information from the web search for use in the action."
|
|
1350
|
+
label: "Summary of Web Search"
|
|
1351
|
+
is_displayable: False
|
|
1352
|
+
filter_from_agent: False
|
|
1353
|
+
complex_data_type_name: "lightning__richTextType"
|
|
1354
|
+
"citationSources": object
|
|
1355
|
+
description: "Source links from the retrieved content used in the web search summary."
|
|
1356
|
+
label: "Citation Sources"
|
|
1357
|
+
is_displayable: False
|
|
1358
|
+
filter_from_agent: False
|
|
1359
|
+
complex_data_type_name: "@apexClassType/AiCopilot__GenAiCitationInput"
|
|
1360
|
+
Get_Record_Research:
|
|
1361
|
+
description: "Queries AiResearchPromptResult for records from the last 30 days that are associated with a specific record ID. Then, summarizes the retrieved insights and research data for that record."
|
|
1362
|
+
label: "Get Record Research"
|
|
1363
|
+
require_user_confirmation: False
|
|
1364
|
+
include_in_progress_indicator: True
|
|
1365
|
+
progress_indicator_message: "Researching"
|
|
1366
|
+
source: "SalesMgmt__GetRecordResearch"
|
|
1367
|
+
target: "flow://SalesMgmt__GetRecordResearch"
|
|
1368
|
+
inputs:
|
|
1369
|
+
"recordId": string
|
|
1370
|
+
description: "The unique ID of the record to research. There can only be one value for this ID."
|
|
1371
|
+
label: "Record ID"
|
|
1372
|
+
is_required: True
|
|
1373
|
+
is_user_input: True
|
|
1374
|
+
outputs:
|
|
1375
|
+
"aiResearchPromptResultRecords": list[object]
|
|
1376
|
+
description: "A collection of AiResearchPromptResult records from the last 30 days associated with the specified record ID. Contains research insights, analysis results, and generated content related to the specified record."
|
|
1377
|
+
label: "AI Research Prompt Result Records"
|
|
1378
|
+
is_displayable: False
|
|
1379
|
+
filter_from_agent: False
|
|
1380
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
1381
|
+
|
|
1382
|
+
subagent Pipeline_Management_Specialist:
|
|
1383
|
+
label: "Pipeline Management Specialist"
|
|
1384
|
+
description: "Specialist subagent for suggesting field updates and providing recommendations for Opportunity records only"
|
|
1385
|
+
before_reasoning:
|
|
1386
|
+
if @variables.currentRecordId is not None and @variables.currentRecordId != "":
|
|
1387
|
+
set @variables.mainRecordId = @variables.currentRecordId
|
|
1388
|
+
reasoning:
|
|
1389
|
+
instructions: ->
|
|
1390
|
+
| You are the Pipeline Management Specialist. You can ONLY suggest Next Step or Stage field values for Opportunity records. You have NO capability to handle anything else.
|
|
1391
|
+
|
|
1392
|
+
# INTENT GATE — EVERY TURN, BEFORE ANYTHING ELSE
|
|
1393
|
+
On EVERY new user message (including mid-conversation), classify the user's CURRENT message into one of three categories:
|
|
1394
|
+
|
|
1395
|
+
## Category A: Slack Button Accept/Reject (check FIRST)
|
|
1396
|
+
If the message matches the pattern "I accept the suggested update for the [field] field for the Opportunity with Id: [006...] and AiGenActionItem with Id: [1Rm...]" OR "I do not accept the suggested update for the [field] field for the Opportunity with Id: [006...] and AiGenActionItem with Id: [1Rm...]":
|
|
1397
|
+
- This is a Slack recommendation button click
|
|
1398
|
+
- Extract: recordId = 18-char ID after "Opportunity with Id:", aiGenActionItemId = 18-char ID after "AiGenActionItem with Id:"
|
|
1399
|
+
- NOTE: before_reasoning may have run Identify_Record_by_Name — ignore its result entirely for Category A messages.
|
|
1400
|
+
- IF message contains "I do not accept": Jump DIRECTLY to STEP 7 Reject path. DO NOT call Set_MainRecordId, Set_AiGenActionItemId, or Set_Intent. Skip STEPs 1-6 entirely. Your first and only action is Reject_Suggestion.
|
|
1401
|
+
- IF message contains "I accept": Call {!@actions.Set_MainRecordId} with mainRecordId = [extracted 006... ID]. Call Set_Intent with currentIntentCode = "field_update_suggestion". Jump DIRECTLY to STEP 7 Accept path (skip STEPs 1-6 entirely).
|
|
1402
|
+
|
|
1403
|
+
## Category B: Field Update Suggestion Request
|
|
1404
|
+
Is the current message about suggesting or updating Next Step or Stage for an Opportunity?
|
|
1405
|
+
- YES (e.g., "suggest next step for X", "recommend stage", "1", "2", a record selection number, "yes", "accept") → call Set_Intent with currentIntentCode = "field_update_suggestion", then proceed to STEP 1 below.
|
|
1406
|
+
|
|
1407
|
+
## Category C: Out of Scope (HARD — no exceptions)
|
|
1408
|
+
- NO → call {!@actions.Return_to_Hub} as your ONE AND ONLY action. Do NOT write ANY text. Do NOT explain. Do NOT ask questions. Do NOT call Clear_Intent first. Do NOT reason further. STOP immediately.
|
|
1409
|
+
|
|
1410
|
+
Examples that are Category C (call Return_to_Hub only, zero text):
|
|
1411
|
+
- "POV for Meta" → Category C.
|
|
1412
|
+
- "meeting prep for Acme" → Category C.
|
|
1413
|
+
- "meeting prep for John Gardner" → Category C.
|
|
1414
|
+
- "brief on Tesla" → Category C.
|
|
1415
|
+
- "conversation insights" → Category C.
|
|
1416
|
+
- "account plan for X" → Category C.
|
|
1417
|
+
- "news about X" → Category C.
|
|
1418
|
+
- "research X" → Category C.
|
|
1419
|
+
- Any request about briefs, research, meeting prep, news, web search, account plan, conversation insights, account overview, snapshot → Category C.
|
|
1420
|
+
|
|
1421
|
+
CRITICAL: Even if you previously showed a disambiguation list and are waiting for a selection, if the user's new message is a completely different request (not a number or record selection), apply the INTENT GATE above. Do NOT assume it is a follow-up to the previous request.
|
|
1422
|
+
|
|
1423
|
+
# Your Capabilities (ONLY these)
|
|
1424
|
+
|
|
1425
|
+
- Resolve record names via CRM_Record_Retriever
|
|
1426
|
+
- Suggest Next Step via Suggest_Opportunity_Next_Step
|
|
1427
|
+
- Suggest Stage via Suggest_Opportunity_Stage
|
|
1428
|
+
- Accept suggestion via CRM_Record_Management_Utility
|
|
1429
|
+
- Return to Hub via Return_to_Hub (for anything you cannot handle)
|
|
1430
|
+
|
|
1431
|
+
# Strict Limitations
|
|
1432
|
+
|
|
1433
|
+
- NEVER output text before calling a tool. First output is always a tool call.
|
|
1434
|
+
- If CRM_Record_Retriever shows the record is NOT an Opportunity → call `Return_to_Hub`. STOP.
|
|
1435
|
+
- Never claim to perform actual updates - only provide recommendations
|
|
1436
|
+
|
|
1437
|
+
CRITICAL: Call tools silently. Only output text when presenting final results. NO confirmations.
|
|
1438
|
+
|
|
1439
|
+
STEP 1: Resolve Record (if needed)
|
|
1440
|
+
- If {!@variables.mainRecordId} is already set (not empty) → Proceed to STEP 2
|
|
1441
|
+
- If {!@variables.mainRecordId} is empty AND {!@variables.currentRecordName} is not empty → Call {!@actions.Identify_Record_by_Name} ONCE with recordName={!@variables.currentRecordName} and objectApiName="Opportunity". Call it EXACTLY ONCE — never again.
|
|
1442
|
+
* If exactly 1 result returned → Call {!@actions.Set_MainRecordId} with that record's id. Proceed to STEP 2.
|
|
1443
|
+
* If multiple results returned (selection UI shown) → Wait for user to pick. When user selects (e.g., "2"), extract the 18-character recordId from that line. Call {!@actions.Set_MainRecordId} with that id. Proceed to STEP 2.
|
|
1444
|
+
* If 0 results returned → output to user: "I couldn't find an Opportunity named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP.
|
|
1445
|
+
- If both are empty → output to user: "I couldn't find an Opportunity named {!@variables.currentRecordName}. Please check the name and try again." Then call {!@actions.Clear_Intent} and STOP.
|
|
1446
|
+
|
|
1447
|
+
STEP 2: Verify Opportunity
|
|
1448
|
+
|
|
1449
|
+
Check if the resolved record is an Opportunity:
|
|
1450
|
+
- Extract ObjectType from the Identify_Record_by_Name response (if it was called)
|
|
1451
|
+
- If ObjectType is NOT "Opportunity":
|
|
1452
|
+
Output: "I can only provide field update suggestions for Opportunity records. This is a [ObjectType] record."
|
|
1453
|
+
Call Return_to_Hub. STOP.
|
|
1454
|
+
- If ObjectType IS "Opportunity" OR {!@variables.currentObjectType} == "Opportunity":
|
|
1455
|
+
Proceed to STEP 3.
|
|
1456
|
+
|
|
1457
|
+
STEP 3: Call Suggest Action
|
|
1458
|
+
|
|
1459
|
+
Silently call the appropriate action with the recordId:
|
|
1460
|
+
- If user requested "next step" → Call Suggest_Opportunity_Next_Step and REMEMBER you called Next Step action
|
|
1461
|
+
- If user requested "stage" → Call Suggest_Opportunity_Stage and REMEMBER you called Stage action
|
|
1462
|
+
- If general request → Call Suggest_Opportunity_Next_Step (default) and REMEMBER you called Next Step action
|
|
1463
|
+
|
|
1464
|
+
CRITICAL: Use the EXACT 18-character recordId from STEP 2 - DO NOT use placeholder IDs
|
|
1465
|
+
|
|
1466
|
+
TRACK WHICH ACTION YOU CALLED: Remember whether you called SuggestOppNextStep or SuggestOppStage - this determines which field to update in STEP 7.
|
|
1467
|
+
|
|
1468
|
+
STEP 4: Check Action Response
|
|
1469
|
+
|
|
1470
|
+
After calling the suggest action, check the response:
|
|
1471
|
+
|
|
1472
|
+
- If action returns null, empty, or error:
|
|
1473
|
+
Output to user: "I don't have enough information about this opportunity to suggest an update to the [Next Step/Stage] field. This could be because there aren't enough emails, calls, notes, or conversation intelligence associated with this opportunity for me to make a recommendation."
|
|
1474
|
+
STOP. Do not proceed.
|
|
1475
|
+
|
|
1476
|
+
- If action returns valid JSON with data:
|
|
1477
|
+
Parse the JSON (suggestedNewValue, originalValue, reasoning) and proceed to STEP 5.
|
|
1478
|
+
|
|
1479
|
+
STEP 5: Present Results (FINAL STEP — after presenting, STOP and wait for user. Do NOT call any action, do NOT transition anywhere.)
|
|
1480
|
+
|
|
1481
|
+
Present the parsed data per OUTPUT FORMAT below.
|
|
1482
|
+
|
|
1483
|
+
OUTPUT FORMAT:
|
|
1484
|
+
|
|
1485
|
+
Present the suggestion conversationally using this exact format:
|
|
1486
|
+
|
|
1487
|
+
"Here's a suggestion for updating the [Next Step/Stage] field for the opportunity *[opportunity name from the suggest action output or Identify_Record_by_Name result]*:"
|
|
1488
|
+
|
|
1489
|
+
For Next Step suggestions (from Suggest_Opportunity_Next_Step):
|
|
1490
|
+
- **Current Next Step:** [from "originalValue" — omit this bullet entirely if value is a placeholder like "[Provide:{NEXT STEP}]"]
|
|
1491
|
+
- **Suggested Next Step:** [from "suggestedNewValue"]
|
|
1492
|
+
- **Reasoning:** [from "reasoning"]
|
|
1493
|
+
|
|
1494
|
+
For Stage suggestions (from Suggest_Opportunity_Stage):
|
|
1495
|
+
- **Current Stage:** [from "originalValue" — omit this bullet entirely if value is a placeholder]
|
|
1496
|
+
- **Suggested Stage:** [from "suggestedNewValue"]
|
|
1497
|
+
- **Reasoning:** [from "reasoning"]
|
|
1498
|
+
|
|
1499
|
+
After the bullet list, on a new line:
|
|
1500
|
+
"Would you like to accept this suggestion and update the field?"
|
|
1501
|
+
|
|
1502
|
+
STEP 7: Handle User Response
|
|
1503
|
+
|
|
1504
|
+
# IF this is a Slack button click (Category A):
|
|
1505
|
+
# The user message contains "for the Opportunity with Id:" and "AiGenActionItem with Id:"
|
|
1506
|
+
|
|
1507
|
+
IF the user message matches Category A (contains "Opportunity with Id:" and "AiGenActionItem with Id:"):
|
|
1508
|
+
|
|
1509
|
+
Extract from the user message:
|
|
1510
|
+
- recordId = the 18-char alphanumeric ID after "Opportunity with Id:" (stop at first non-alphanumeric character: period, comma, space, or end of string)
|
|
1511
|
+
- aiGenActionItemId = the 18-char alphanumeric ID after "AiGenActionItem with Id:" (stop at first non-alphanumeric character: period, comma, space, or end of string)
|
|
1512
|
+
IMPORTANT: Strip any trailing punctuation (e.g., ".Please process this change accordingly." or similar) from both IDs before using them.
|
|
1513
|
+
|
|
1514
|
+
IF message contains "I do not accept":
|
|
1515
|
+
Call Reject_Suggestion: "action: reject, aiGenActionItemId: " + aiGenActionItemId
|
|
1516
|
+
After Reject_Suggestion returns → respond: "The suggestion has been declined." Call Clear_Intent. Then call Return_to_Hub. STOP.
|
|
1517
|
+
|
|
1518
|
+
IF message contains "I accept":
|
|
1519
|
+
Look at the "Action Required:" line in the notification. It says something like:
|
|
1520
|
+
"Action Required: Would you like to update the NextStep field to Conduct demo for SDR package with CEO and Sales team. Propose new pricing deals during the demo this week.?"
|
|
1521
|
+
|
|
1522
|
+
The text of that line after "Would you like to update the " contains the field name followed by " field to " followed by the new value followed by "?".
|
|
1523
|
+
|
|
1524
|
+
Read the actual "Action Required:" line in this conversation's notification. Do not use a placeholder or variable. Copy the real text.
|
|
1525
|
+
|
|
1526
|
+
If the notification "Action Required:" line says "Would you like to update the NextStep field to Conduct demo for SDR package with CEO and Sales team. Propose new pricing deals during the demo this week.?" then:
|
|
1527
|
+
- field is NextStep → normalized to Next Step
|
|
1528
|
+
- new value is the text "Conduct demo for SDR package with CEO and Sales team. Propose new pricing deals during the demo this week."
|
|
1529
|
+
- userInput = "Update Next Step field to: Conduct demo for SDR package with CEO and Sales team. Propose new pricing deals during the demo this week."
|
|
1530
|
+
|
|
1531
|
+
If the notification "Action Required:" line says "Would you like to update the StageName field to Proposal/Price Quote?" then:
|
|
1532
|
+
- field is StageName → normalized to Stage
|
|
1533
|
+
- new value is the text "Proposal/Price Quote"
|
|
1534
|
+
- userInput = "Update Stage field to: Proposal/Price Quote"
|
|
1535
|
+
|
|
1536
|
+
Normalize: StageName → Stage, NextStep → Next Step. Do NOT write any placeholder like [VALUE] or [FIELDNAME] anywhere in userInput.
|
|
1537
|
+
|
|
1538
|
+
Call Accept_Suggestion:
|
|
1539
|
+
"action: accept, recordId: " + recordId + ", objectType: Opportunity, userInput: " + userInput + ", aiGenActionItemId: " + aiGenActionItemId
|
|
1540
|
+
After Accept_Suggestion returns → tell user the field was updated. Call Clear_Intent. Then call Return_to_Hub. STOP.
|
|
1541
|
+
|
|
1542
|
+
# ELSE this is a non-Slack response (Category B — suggestion was in this conversation):
|
|
1543
|
+
|
|
1544
|
+
ELSE:
|
|
1545
|
+
|
|
1546
|
+
IF user rejects ("no", "don't update", "skip", "cancel"):
|
|
1547
|
+
Call Reject_Suggestion: "action: reject, aiGenActionItemId: "
|
|
1548
|
+
Call Clear_Intent. Then call Return_to_Hub. STOP.
|
|
1549
|
+
|
|
1550
|
+
IF user accepts ("yes", "accept", "update it", "go ahead"):
|
|
1551
|
+
From your own previous message, extract the suggested value:
|
|
1552
|
+
- From the "- **Suggested Next Step:**" bullet → use that value, field = "Next Step"
|
|
1553
|
+
- From the "- **Suggested Stage:**" bullet → use that value, field = "Stage"
|
|
1554
|
+
|
|
1555
|
+
Call Accept_Suggestion:
|
|
1556
|
+
"action: accept, recordId: " + {!@variables.mainRecordId} + ", objectType: Opportunity, userInput: Update " + field + " field to: " + value
|
|
1557
|
+
After Accept_Suggestion returns → tell user the field was updated. Call Clear_Intent. Then call Return_to_Hub. STOP.
|
|
1558
|
+
|
|
1559
|
+
# Output Rules
|
|
1560
|
+
- Never expose AiGenActionItem IDs, record IDs, or internal action names to the user.
|
|
1561
|
+
- Use opportunity name (not ID) in user-facing messages.
|
|
1562
|
+
- After completing update or rejection, ALWAYS call Clear_Intent.
|
|
1563
|
+
|
|
1564
|
+
actions:
|
|
1565
|
+
Identify_Record_by_Name: @actions.Identify_Record_by_Name
|
|
1566
|
+
with recordName=...
|
|
1567
|
+
with objectApiName=...
|
|
1568
|
+
Set_Intent: @utils.setVariables
|
|
1569
|
+
with currentIntentCode = ...
|
|
1570
|
+
Clear_Intent: @utils.setVariables
|
|
1571
|
+
with currentIntentCode = ""
|
|
1572
|
+
with currentRecordName = ""
|
|
1573
|
+
with currentObjectType = ""
|
|
1574
|
+
with currentAccountName = ""
|
|
1575
|
+
with mainRecordId = ""
|
|
1576
|
+
with currentAccountId = ""
|
|
1577
|
+
Set_MainRecordId: @utils.setVariables
|
|
1578
|
+
with mainRecordId = ...
|
|
1579
|
+
Suggest_Opportunity_Stage: @actions.SuggestOppStage
|
|
1580
|
+
with "Input:Opportunity" = ...
|
|
1581
|
+
Suggest_Opportunity_Next_Step: @actions.SuggestOppNextStep
|
|
1582
|
+
with "Input:Opportunity" = ...
|
|
1583
|
+
Accept_Suggestion: @subagent.CRM_Record_Management_Utility
|
|
1584
|
+
description: "Upon user acceptance of field update suggestion, invoke CRM Record Management Utility to perform the actual update with AiGenActionItem lifecycle management. Pass request in format: 'action: accept, recordId: [18-char ID], objectType: Opportunity, userInput: [complete field update statement], aiGenActionItemId: [ID from context]'. CRITICAL: The userInput must be the COMPLETE field update statement with the ENTIRE suggested value, no truncation. The utility will verify AiGenActionItem state, extract fields, update the record, and mark the action item as completed."
|
|
1585
|
+
Reject_Suggestion: @subagent.CRM_Record_Management_Utility
|
|
1586
|
+
description: "Upon user rejection of field update suggestion, invoke CRM Record Management Utility to handle AiGenActionItem rejection. Pass request in format: 'action: reject, aiGenActionItemId: [ID from context]'. The utility will verify AiGenActionItem state and mark it as REJECTED. No record update is performed."
|
|
1587
|
+
Return_to_Hub: @utils.transition to @subagent.Sales_Management_Router
|
|
1588
|
+
description: "Return control to Hub Router when the record is not an Opportunity or the request is out of scope"
|
|
1589
|
+
actions:
|
|
1590
|
+
Identify_Record_by_Name:
|
|
1591
|
+
description: "Searches for Salesforce CRM records by name and returns a list of matching Salesforce CRM record IDs."
|
|
1592
|
+
label: "Identify Record by Name"
|
|
1593
|
+
require_user_confirmation: False
|
|
1594
|
+
include_in_progress_indicator: True
|
|
1595
|
+
source: "EmployeeCopilot__IdentifyRecordByName"
|
|
1596
|
+
target: "standardInvocableAction://identifyRecordByName"
|
|
1597
|
+
inputs:
|
|
1598
|
+
"recordName": string
|
|
1599
|
+
description: "The name or title of the Salesforce record extracted from user input. The name can be used to find a Salesforce CRM record."
|
|
1600
|
+
label: "recordName"
|
|
1601
|
+
is_required: True
|
|
1602
|
+
is_user_input: True
|
|
1603
|
+
"objectApiName": string
|
|
1604
|
+
description: "The API name of the Salesforce object (such as Account or Opportunity) associated with the record the user wants to find. The name can be obtained from the context, like with [object_type] if present and does not always require the identifyObjectByName action. (eg. IdentifyRecordByName(recordName=\"Acme\", objectApiName=\"Account\")). Only if the object name can't be identified, leave this empty."
|
|
1605
|
+
label: "objectApiName"
|
|
1606
|
+
is_required: False
|
|
1607
|
+
is_user_input: False
|
|
1608
|
+
outputs:
|
|
1609
|
+
"searchResults": list[object]
|
|
1610
|
+
description: "A list of the matching Salesforce record ids in descending order of relevance."
|
|
1611
|
+
label: "searchResults"
|
|
1612
|
+
is_displayable: False
|
|
1613
|
+
filter_from_agent: False
|
|
1614
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
1615
|
+
SuggestOppStage:
|
|
1616
|
+
description: "Identifies and suggests the appropriate stage for an opportunity."
|
|
1617
|
+
inputs:
|
|
1618
|
+
"Input:Opportunity": object
|
|
1619
|
+
description: "The record ID of the opportunity to suggest the stage for. There can be only one value for the record ID."
|
|
1620
|
+
label: "Opportunity Record ID"
|
|
1621
|
+
is_required: True
|
|
1622
|
+
is_user_input: False
|
|
1623
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
1624
|
+
outputs:
|
|
1625
|
+
opportunityStage: string
|
|
1626
|
+
description: "A JSON string describing the stage suggested for an opportunity. The suggestion is based on relevant opportunity information such as emails, voice and video calls, and content notes associated with the opportunity."
|
|
1627
|
+
label: "Opportunity Stage"
|
|
1628
|
+
filter_from_agent: False
|
|
1629
|
+
is_displayable: False
|
|
1630
|
+
target: "generatePromptResponse://sales_pipe_mgmt__RecommendStageforOpp"
|
|
1631
|
+
label: "Suggest Stage for Opportunity"
|
|
1632
|
+
require_user_confirmation: False
|
|
1633
|
+
include_in_progress_indicator: True
|
|
1634
|
+
progress_indicator_message: "Suggesting Stage for the Opportunity"
|
|
1635
|
+
source: "sales_pipe_mgmt__SuggestOppStage"
|
|
1636
|
+
SuggestOppNextStep:
|
|
1637
|
+
description: "Identifies and suggests a value for the Next Step field on an opportunity."
|
|
1638
|
+
inputs:
|
|
1639
|
+
"Input:Opportunity": object
|
|
1640
|
+
description: "The record ID of the opportunity to suggest the next step for. There can be only one value for the record ID."
|
|
1641
|
+
label: "Opportunity Record ID"
|
|
1642
|
+
is_required: True
|
|
1643
|
+
is_user_input: False
|
|
1644
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
1645
|
+
outputs:
|
|
1646
|
+
opportunityNextStep: string
|
|
1647
|
+
description: "A JSON string describing the next step suggested for an opportunity. The suggestion is based on relevant opportunity information such as emails, voice and video calls, and content notes associated with the opportunity."
|
|
1648
|
+
label: "Opportunity Next Step"
|
|
1649
|
+
filter_from_agent: False
|
|
1650
|
+
is_displayable: False
|
|
1651
|
+
target: "generatePromptResponse://sales_pipe_mgmt__RecommendNextStepforOpp"
|
|
1652
|
+
label: "Suggest Next Step for Opportunity"
|
|
1653
|
+
require_user_confirmation: False
|
|
1654
|
+
include_in_progress_indicator: True
|
|
1655
|
+
progress_indicator_message: "Suggesting Next Step for the Opportunity"
|
|
1656
|
+
source: "sales_pipe_mgmt__SuggestOppNextStep"
|
|
1657
|
+
|
|
1658
|
+
subagent Customer_Communication:
|
|
1659
|
+
label: "Communicate With Customers"
|
|
1660
|
+
description: "Questions related to a user's query to get personalized and crisp communication involving written contact and communication such as email or follow ups with a customer. This could involve sending meeting requests, scheduling a meeting via email, writing to a contact, or creating documents for customers."
|
|
1661
|
+
before_reasoning:
|
|
1662
|
+
if (@variables.mainRecordId is None or @variables.mainRecordId == "") and @variables.currentRecordName != "":
|
|
1663
|
+
run @actions.IdentifyRecordByName
|
|
1664
|
+
with recordName = @variables.currentRecordName
|
|
1665
|
+
with objectApiName = @variables.currentObjectType
|
|
1666
|
+
set @variables.mainRecordId = @outputs.searchResults[0].id if @outputs.searchResults.length == 1 else ""
|
|
1667
|
+
reasoning:
|
|
1668
|
+
instructions: ->
|
|
1669
|
+
| ## Scope Check (evaluate FIRST on every new user message)
|
|
1670
|
+
Is the user's current message about drafting/sending/revising an email, scheduling a meeting, or sending a meeting invite?
|
|
1671
|
+
- YES → continue below and handle the request.
|
|
1672
|
+
- NO (e.g. "meeting prep", "POV", "account plan", "suggest next step", "conversation insights", or any research/record/pipeline request) → call {!@actions.Return_to_Hub} as your ONLY action. STOP. Do NOT attempt to handle it here.
|
|
1673
|
+
|
|
1674
|
+
The recipient's record ID may already be resolved in {!@variables.mainRecordId} via before_reasoning. If {!@variables.mainRecordId} is set, use it directly as the recordId — do NOT call IdentifyRecordByName again. If {!@variables.mainRecordId} is empty (e.g. multiple matches or no match found), call IdentifyRecordByName to resolve the recipient. Do not ask the user for the record ID.
|
|
1675
|
+
Only if a user uses phrases such as "schedule meeting", "set up meeting", "book follow-up", "share time slots", "provide calendar availability", "send a meeting invite" or similar variants using the same vocabulary should Send Meeting Request be triggered.
|
|
1676
|
+
If the user specifies a product and/or brand, you must not remove it from the user input and include it when passing the user input to the action.
|
|
1677
|
+
For follow-up emails, do not request additional details. Instead, treat the provided utterance as the user's input.
|
|
1678
|
+
Always show the drafted email by referencing the generated email in action output.
|
|
1679
|
+
actions:
|
|
1680
|
+
Return_to_Hub: @utils.transition to @subagent.Sales_Management_Router
|
|
1681
|
+
description: "Return to the Router when the request is out of scope for Customer Communication (e.g. meeting prep, POV, research, pipeline suggestions)."
|
|
1682
|
+
IdentifyRecordByName: @actions.IdentifyRecordByName
|
|
1683
|
+
with recordName=...
|
|
1684
|
+
with objectApiName=...
|
|
1685
|
+
QueryRecords: @actions.QueryRecords
|
|
1686
|
+
with query=...
|
|
1687
|
+
with customizationInstructions=...
|
|
1688
|
+
DraftOrReviseEmail: @actions.DraftOrReviseEmail
|
|
1689
|
+
with recordId=...
|
|
1690
|
+
with additionalRecordIds=...
|
|
1691
|
+
with userInput=...
|
|
1692
|
+
with supportiveTextualData=...
|
|
1693
|
+
with latestEmailDraft=...
|
|
1694
|
+
SendMeetingRequest: @actions.SendMeetingRequest
|
|
1695
|
+
with recordId=...
|
|
1696
|
+
with userInput=...
|
|
1697
|
+
with meetingDate=...
|
|
1698
|
+
with startTime=...
|
|
1699
|
+
actions:
|
|
1700
|
+
IdentifyRecordByName:
|
|
1701
|
+
description: "Searches for Salesforce CRM records by name and returns a list of matching Salesforce CRM record IDs."
|
|
1702
|
+
label: "Identify Record by Name"
|
|
1703
|
+
require_user_confirmation: False
|
|
1704
|
+
include_in_progress_indicator: True
|
|
1705
|
+
source: "EmployeeCopilot__IdentifyRecordByName"
|
|
1706
|
+
target: "standardInvocableAction://identifyRecordByName"
|
|
1707
|
+
inputs:
|
|
1708
|
+
"recordName": string
|
|
1709
|
+
description: "The name or title of the Salesforce record extracted from user input."
|
|
1710
|
+
label: "recordName"
|
|
1711
|
+
is_required: True
|
|
1712
|
+
is_user_input: True
|
|
1713
|
+
"objectApiName": string
|
|
1714
|
+
description: "The API name of the Salesforce object (such as Contact). Leave empty if unknown."
|
|
1715
|
+
label: "objectApiName"
|
|
1716
|
+
is_required: False
|
|
1717
|
+
is_user_input: False
|
|
1718
|
+
outputs:
|
|
1719
|
+
"searchResults": list[object]
|
|
1720
|
+
description: "A list of the matching Salesforce record ids in descending order of relevance."
|
|
1721
|
+
label: "searchResults"
|
|
1722
|
+
is_displayable: True
|
|
1723
|
+
filter_from_agent: False
|
|
1724
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
1725
|
+
QueryRecords:
|
|
1726
|
+
description: "Finds and retrieves Salesforce CRM records based on a natural-language request and specific conditions."
|
|
1727
|
+
label: "Query Records"
|
|
1728
|
+
require_user_confirmation: False
|
|
1729
|
+
include_in_progress_indicator: True
|
|
1730
|
+
progress_indicator_message: "Querying records"
|
|
1731
|
+
source: "EmployeeCopilot__QueryRecords"
|
|
1732
|
+
target: "standardInvocableAction://queryRecords"
|
|
1733
|
+
inputs:
|
|
1734
|
+
"query": string
|
|
1735
|
+
description: "The question or request in plain English."
|
|
1736
|
+
label: "Query"
|
|
1737
|
+
is_required: True
|
|
1738
|
+
is_user_input: True
|
|
1739
|
+
"customizationInstructions": string
|
|
1740
|
+
description: "Optional custom instructions to optimize the query."
|
|
1741
|
+
label: "Customization Instructions"
|
|
1742
|
+
is_required: False
|
|
1743
|
+
is_user_input: False
|
|
1744
|
+
outputs:
|
|
1745
|
+
"result": list[object]
|
|
1746
|
+
description: "A list of matching Salesforce CRM records with details about each record."
|
|
1747
|
+
label: "Result"
|
|
1748
|
+
is_displayable: True
|
|
1749
|
+
filter_from_agent: False
|
|
1750
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
1751
|
+
DraftOrReviseEmail:
|
|
1752
|
+
description: |
|
|
1753
|
+
Creates an email draft or revises the latest generated email based on user input.
|
|
1754
|
+
label: "Draft or Revise Email"
|
|
1755
|
+
require_user_confirmation: False
|
|
1756
|
+
include_in_progress_indicator: True
|
|
1757
|
+
progress_indicator_message: "Crafting your email"
|
|
1758
|
+
source: "EmployeeCopilot__DraftOrReviseEmail"
|
|
1759
|
+
target: "standardInvocableAction://getDraftOrReviseEmailPrompt"
|
|
1760
|
+
inputs:
|
|
1761
|
+
"recordId": object
|
|
1762
|
+
description: |
|
|
1763
|
+
The primary Salesforce record ID used to retrieve the main recipient's email address. For revised emails, the record ID is retrieved from the conversation history.
|
|
1764
|
+
label: "Record ID"
|
|
1765
|
+
is_required: True
|
|
1766
|
+
is_user_input: False
|
|
1767
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
1768
|
+
"additionalRecordIds": list[object]
|
|
1769
|
+
description: |
|
|
1770
|
+
Optional list of additional Salesforce record IDs to retrieve more recipients' email addresses.
|
|
1771
|
+
label: "Additional Record IDs"
|
|
1772
|
+
is_required: False
|
|
1773
|
+
is_user_input: False
|
|
1774
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
1775
|
+
"userInput": string
|
|
1776
|
+
description: |
|
|
1777
|
+
A string provided by the user with instructions for drafting or revising the email.
|
|
1778
|
+
label: "User Input"
|
|
1779
|
+
is_required: True
|
|
1780
|
+
is_user_input: True
|
|
1781
|
+
"supportiveTextualData": string
|
|
1782
|
+
description: |
|
|
1783
|
+
Textual data relevant to drafting the email, including conversation history and results from prior actions.
|
|
1784
|
+
label: "Supportive Textual Data"
|
|
1785
|
+
is_required: False
|
|
1786
|
+
is_user_input: False
|
|
1787
|
+
"latestEmailDraft": object
|
|
1788
|
+
description: |
|
|
1789
|
+
The latest version of the email draft. Required only when revising a previous draft. Retrieve the draft from the conversation history.
|
|
1790
|
+
label: "Latest Email Draft"
|
|
1791
|
+
is_required: False
|
|
1792
|
+
is_user_input: False
|
|
1793
|
+
complex_data_type_name: "lightning__emailType"
|
|
1794
|
+
outputs:
|
|
1795
|
+
"generatedEmail": object
|
|
1796
|
+
description: |
|
|
1797
|
+
The recipient ID, subject, and body of the email.
|
|
1798
|
+
label: "Generated Email"
|
|
1799
|
+
is_displayable: True
|
|
1800
|
+
filter_from_agent: False
|
|
1801
|
+
complex_data_type_name: "lightning__emailType"
|
|
1802
|
+
SendMeetingRequest:
|
|
1803
|
+
description: |
|
|
1804
|
+
Drafts a message to the recipient with possible time slots or a link to access the calendar of the sender to schedule a sales meeting.
|
|
1805
|
+
label: "Send Meeting Request"
|
|
1806
|
+
require_user_confirmation: True
|
|
1807
|
+
include_in_progress_indicator: True
|
|
1808
|
+
progress_indicator_message: "Drafting your meeting request"
|
|
1809
|
+
source: "Sales__SendMeetingRequest"
|
|
1810
|
+
target: "standardInvocableAction://sendMeetingRequest"
|
|
1811
|
+
inputs:
|
|
1812
|
+
"recordId": object
|
|
1813
|
+
description: |
|
|
1814
|
+
The Salesforce record ID of the person who is the recipient of the generated message.
|
|
1815
|
+
label: "Record ID"
|
|
1816
|
+
is_required: True
|
|
1817
|
+
is_user_input: False
|
|
1818
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
1819
|
+
"userInput": string
|
|
1820
|
+
description: |
|
|
1821
|
+
User instructions for drafting a message to schedule a sales meeting.
|
|
1822
|
+
label: "User Input"
|
|
1823
|
+
is_required: False
|
|
1824
|
+
is_user_input: False
|
|
1825
|
+
"meetingDate": date
|
|
1826
|
+
description: |
|
|
1827
|
+
Extracts the meeting date from the user's input and converts it into YYYY-MM-DD format. If the user explicitly mentions the date of the meeting, extract it from the input. But if the user requests the next available date or doesn't mention the date at all, don't prompt the user for the date again. If the user doesn't specify the date, the default value is null.
|
|
1828
|
+
label: "Meeting Date"
|
|
1829
|
+
is_required: False
|
|
1830
|
+
is_user_input: False
|
|
1831
|
+
complex_data_type_name: "lightning__dateType"
|
|
1832
|
+
"startTime": object
|
|
1833
|
+
description: |
|
|
1834
|
+
Extracts the meeting start time from the user's input and converts it into HH:MM format. If the user explicitly mentions the meeting start time, extract it from the input. But if the user requests the next available meeting time or doesn't mention the meeting start time at all, don't prompt the user for the start time again. If the user doesn't specify the time of day, the default value is null.
|
|
1835
|
+
label: "Start Time"
|
|
1836
|
+
is_required: False
|
|
1837
|
+
is_user_input: False
|
|
1838
|
+
complex_data_type_name: "lightning__timeType"
|
|
1839
|
+
outputs:
|
|
1840
|
+
"generatedEmail": object
|
|
1841
|
+
description: |
|
|
1842
|
+
A JSON object that contains the recipient ID, subject, and the draft of the body of the meeting request.
|
|
1843
|
+
label: "Generated Meeting Email"
|
|
1844
|
+
is_displayable: True
|
|
1845
|
+
filter_from_agent: False
|
|
1846
|
+
complex_data_type_name: "lightning__emailType"
|
|
1847
|
+
|
|
1848
|
+
subagent CRM_Record_Management_Utility:
|
|
1849
|
+
description: "Utility subagent that handles CRM record field updates. Extracts fields and values from user input, then updates Salesforce CRM records. Also manages AiGenActionItem lifecycle for Slack-initiated field update suggestions."
|
|
1850
|
+
reasoning:
|
|
1851
|
+
instructions: ->
|
|
1852
|
+
| You are the CRM Record Management Utility - a utility subagent that performs record field updates.
|
|
1853
|
+
|
|
1854
|
+
# User-Facing Output Rules
|
|
1855
|
+
- NEVER expose recordId, AiGenActionItem IDs, or other internal identifiers in messages shown to the user.
|
|
1856
|
+
- Never reference the Opportunity Id in user-facing text.
|
|
1857
|
+
- Do NOT ask the user for any information. Do NOT retrieve the Opportunity Name before responding. Just execute the steps and confirm completion.
|
|
1858
|
+
|
|
1859
|
+
# Your Role
|
|
1860
|
+
|
|
1861
|
+
This utility is invoked when a user accepts OR rejects a field update suggestion from Pipeline_Management_Specialist.
|
|
1862
|
+
You execute the actual record update by calling the appropriate actions in sequence.
|
|
1863
|
+
When the channel is Slack, you also manage AiGenActionItem state to track the lifecycle of suggestions.
|
|
1864
|
+
|
|
1865
|
+
# Request Format
|
|
1866
|
+
|
|
1867
|
+
Requests arrive in one of two formats:
|
|
1868
|
+
|
|
1869
|
+
Acceptance format:
|
|
1870
|
+
- "action: accept, recordId: [18-char ID], objectType: [object], userInput: [field update statement], aiGenActionItemId: [ID or empty]"
|
|
1871
|
+
|
|
1872
|
+
Rejection format:
|
|
1873
|
+
- "action: reject, aiGenActionItemId: [ID or empty]"
|
|
1874
|
+
|
|
1875
|
+
Legacy format (still supported):
|
|
1876
|
+
- "recordId: {!@variables.mainRecordId}, objectType: {!@variables.currentObjectType}, userInput: [user request with field updates]"
|
|
1877
|
+
|
|
1878
|
+
# Execution Flow: ACCEPTANCE (action = accept)
|
|
1879
|
+
|
|
1880
|
+
Step 1: Parse Request
|
|
1881
|
+
|
|
1882
|
+
From the incoming request, extract parameters:
|
|
1883
|
+
1. recordId: Salesforce record ID (15 or 18 characters starting with 001, 003, 006, etc.)
|
|
1884
|
+
- If invalid or missing, return error: "Invalid recordId provided"
|
|
1885
|
+
2. objectType: Object API name (e.g., "Opportunity", "Account", "Contact")
|
|
1886
|
+
- If missing, return error: "Object type not specified"
|
|
1887
|
+
3. userInput: The COMPLETE field update instruction after "userInput:"
|
|
1888
|
+
- Preserve EXACTLY as provided, no modifications
|
|
1889
|
+
4. aiGenActionItemId: The AiGenActionItem ID (may be empty if not from Slack)
|
|
1890
|
+
|
|
1891
|
+
Step 2: AiGenActionItem State Verification (Slack channel only)
|
|
1892
|
+
|
|
1893
|
+
Condition: Execute this step ONLY when $Context.ChannelType is Slack AND aiGenActionItemId is not empty.
|
|
1894
|
+
If aiGenActionItemId is empty, skip directly to Step 3.
|
|
1895
|
+
|
|
1896
|
+
Call {!@actions.Update_AiGen_ActionItem} with:
|
|
1897
|
+
- aiGenActionItemId: [extracted aiGenActionItemId]
|
|
1898
|
+
- aiGenActionItemStatus: "WAITING"
|
|
1899
|
+
|
|
1900
|
+
Use the returned status strictly for control flow.
|
|
1901
|
+
Errors from this call must not be surfaced to the user.
|
|
1902
|
+
|
|
1903
|
+
Termination check:
|
|
1904
|
+
- If returned status is "COMPLETED_SUCCESS" or "REJECTED":
|
|
1905
|
+
Immediately halt all further tool calls.
|
|
1906
|
+
Respond once with: "An action has already been taken on this opportunity, so no further changes were made."
|
|
1907
|
+
STOP. Do not proceed.
|
|
1908
|
+
- For ANY other returned status (including "WAITING", null, empty, or any other value):
|
|
1909
|
+
Proceed to Step 3.
|
|
1910
|
+
|
|
1911
|
+
Step 3: Extract Field Values
|
|
1912
|
+
|
|
1913
|
+
Call {!@actions.ExtractFieldsAndValuesFromUserInput} with the parsed parameters:
|
|
1914
|
+
- recordId: [extracted recordId from Step 1]
|
|
1915
|
+
- objectType: [extracted objectType from Step 1]
|
|
1916
|
+
- userInput: [extracted userInput from Step 1 - COMPLETE, UNMODIFIED]
|
|
1917
|
+
|
|
1918
|
+
Wait for the action to complete and capture the output.
|
|
1919
|
+
|
|
1920
|
+
Error Check: If ExtractFieldsAndValuesFromUserInput fails or returns empty:
|
|
1921
|
+
- Return error: "Unable to extract field values from the update request"
|
|
1922
|
+
- DO NOT proceed to Step 4
|
|
1923
|
+
|
|
1924
|
+
Step 4: Update the Record (ONLY action in this step)
|
|
1925
|
+
|
|
1926
|
+
Call {!@actions.UpdateRecordFields} with:
|
|
1927
|
+
- recordDetailInput: [the EXACT output from Step 3's recordDetails]
|
|
1928
|
+
|
|
1929
|
+
CRITICAL: Do NOT call any other action in this step. UpdateRecordFields must be the ONLY action called here.
|
|
1930
|
+
Wait for it to complete and check the result.
|
|
1931
|
+
If UpdateRecordFields fails or returns an error:
|
|
1932
|
+
- Return error to user: "Unable to update the record. Please try again."
|
|
1933
|
+
- DO NOT proceed to Step 5. STOP here.
|
|
1934
|
+
|
|
1935
|
+
Only if UpdateRecordFields succeeds and returns recordDetailOutput, proceed to Step 5.
|
|
1936
|
+
|
|
1937
|
+
Step 5: Mark AiGenActionItem as Completed
|
|
1938
|
+
|
|
1939
|
+
Condition: Execute ONLY when aiGenActionItemId is not empty AND Step 4 did not return an error.
|
|
1940
|
+
If aiGenActionItemId is empty, skip directly to Step 6.
|
|
1941
|
+
If Step 4 returned an error message (not a successful update), skip directly to Step 6.
|
|
1942
|
+
|
|
1943
|
+
IMPORTANT: This is a separate tool call from Step 4. Call it AFTER Step 4 has returned.
|
|
1944
|
+
|
|
1945
|
+
Call {!@actions.Update_AiGen_ActionItem} with:
|
|
1946
|
+
- aiGenActionItemId: [extracted aiGenActionItemId from Step 1]
|
|
1947
|
+
- aiGenActionItemStatus: "COMPLETED_SUCCESS"
|
|
1948
|
+
|
|
1949
|
+
This call is internal-only. Never mention or expose it to the user.
|
|
1950
|
+
|
|
1951
|
+
Step 6: Confirm to User
|
|
1952
|
+
|
|
1953
|
+
Output: "The [field_name] field has been updated successfully."
|
|
1954
|
+
|
|
1955
|
+
# Execution Flow: REJECTION (action = reject)
|
|
1956
|
+
|
|
1957
|
+
Step R1: Parse Request
|
|
1958
|
+
|
|
1959
|
+
Extract aiGenActionItemId from the request.
|
|
1960
|
+
|
|
1961
|
+
Step R2: AiGenActionItem State Verification (Slack channel only)
|
|
1962
|
+
|
|
1963
|
+
Condition: Execute ONLY when $Context.ChannelType is Slack AND aiGenActionItemId is not empty.
|
|
1964
|
+
If aiGenActionItemId is empty, skip to Step R4.
|
|
1965
|
+
|
|
1966
|
+
Call {!@actions.Update_AiGen_ActionItem} with:
|
|
1967
|
+
- aiGenActionItemId: [extracted aiGenActionItemId]
|
|
1968
|
+
- aiGenActionItemStatus: "WAITING"
|
|
1969
|
+
|
|
1970
|
+
Use the returned status strictly for control flow.
|
|
1971
|
+
Errors from this call must not be surfaced to the user.
|
|
1972
|
+
|
|
1973
|
+
Termination check:
|
|
1974
|
+
- If returned status is "COMPLETED_SUCCESS" or "REJECTED":
|
|
1975
|
+
Immediately halt all further tool calls.
|
|
1976
|
+
Respond once with: "An action has already been taken on this opportunity, so no further changes were made."
|
|
1977
|
+
STOP. Do not proceed.
|
|
1978
|
+
- For ANY other returned status (including "WAITING", null, empty, or any other value):
|
|
1979
|
+
Proceed to Step R3.
|
|
1980
|
+
|
|
1981
|
+
Step R3: Mark AiGenActionItem as Rejected — MANDATORY TOOL CALL
|
|
1982
|
+
|
|
1983
|
+
⚠ HARD REQUIREMENT: After Step R2 completes with a non-terminal status, Step R3 is NOT optional. You MUST call the tool below. Do NOT output any text. Do NOT skip to R4. Do NOT return control to the caller. Make the tool call NOW.
|
|
1984
|
+
|
|
1985
|
+
Call {!@actions.Update_AiGen_ActionItem} with:
|
|
1986
|
+
- aiGenActionItemId: [extracted aiGenActionItemId]
|
|
1987
|
+
- aiGenActionItemStatus: "REJECTED"
|
|
1988
|
+
|
|
1989
|
+
This call is internal-only. Never mention or expose it to the user.
|
|
1990
|
+
After this tool call completes, proceed to Step R4. There is no valid execution path from R2 directly to R4.
|
|
1991
|
+
|
|
1992
|
+
CRITICAL: Do NOT call ExtractFieldsAndValuesFromUserInput or UpdateRecordFields in rejection scenarios.
|
|
1993
|
+
|
|
1994
|
+
Step R4: Confirm Rejection
|
|
1995
|
+
|
|
1996
|
+
Respond: "Understood. The suggestion has been declined and no changes were made to the opportunity."
|
|
1997
|
+
|
|
1998
|
+
# Atomicity Constraint
|
|
1999
|
+
|
|
2000
|
+
Treat the acceptance flow (Steps 1 → 2 → 3 → 4 → 5 → 6) and rejection flow (Steps R1 → R2 → R3 → R4) as single atomic chains.
|
|
2001
|
+
Do not skip, reorder, retry, or partially execute these steps.
|
|
2002
|
+
CRITICAL ORDERING: Each step must call exactly ONE action and wait for its result before proceeding. Never call two actions in the same step.
|
|
2003
|
+
|
|
2004
|
+
# Visibility Rules
|
|
2005
|
+
|
|
2006
|
+
- Treat ALL Update_AiGen_ActionItem calls as internal-only.
|
|
2007
|
+
- Never mention or expose these calls or their results to the user.
|
|
2008
|
+
- Remove all AiGenActionItem IDs from any text shown to the user.
|
|
2009
|
+
|
|
2010
|
+
# Strict Limitations
|
|
2011
|
+
|
|
2012
|
+
- ONLY field extraction, record updates, and AiGenActionItem state management
|
|
2013
|
+
- ONE ACTION PER TOOL CALL: Call exactly one action at a time. Wait for its result. Then call the next. NEVER batch multiple actions in a single tool call turn.
|
|
2014
|
+
- DO NOT modify the userInput - pass it through EXACTLY as received
|
|
2015
|
+
- DO NOT resolve record names - caller MUST provide recordId
|
|
2016
|
+
- Pure utility - deterministic operations only
|
|
2017
|
+
- Handle errors gracefully and return clear error messages
|
|
2018
|
+
actions:
|
|
2019
|
+
ExtractFieldsAndValuesFromUserInput: @actions.ExtractFieldsAndValuesFromUserInput
|
|
2020
|
+
with recordId=...
|
|
2021
|
+
with objectType=...
|
|
2022
|
+
with userInput=...
|
|
2023
|
+
UpdateRecordFields: @actions.UpdateRecordFields
|
|
2024
|
+
with recordDetailInput=...
|
|
2025
|
+
Update_AiGen_ActionItem: @actions.Update_AiGen_ActionItem
|
|
2026
|
+
with aiGenActionItemId=...
|
|
2027
|
+
with aiGenActionItemStatus=...
|
|
2028
|
+
actions:
|
|
2029
|
+
ExtractFieldsAndValuesFromUserInput:
|
|
2030
|
+
description: "Extracts Salesforce CRM record fields and their corresponding field values from the user input. This action must run before UpdateRecordFields to convert user input to a Salesforce sObject."
|
|
2031
|
+
label: "Extract Fields And Values From User Input"
|
|
2032
|
+
require_user_confirmation: False
|
|
2033
|
+
include_in_progress_indicator: True
|
|
2034
|
+
progress_indicator_message: "Preparing your changes"
|
|
2035
|
+
source: "EmployeeCopilot__ExtractFieldsAndValuesFromUserInput"
|
|
2036
|
+
target: "standardInvocableAction://getRecordFieldsAndValues"
|
|
2037
|
+
inputs:
|
|
2038
|
+
"recordId": object
|
|
2039
|
+
description: "The Salesforce record ID for the record to be updated."
|
|
2040
|
+
label: "Record ID"
|
|
2041
|
+
is_required: True
|
|
2042
|
+
is_user_input: False
|
|
2043
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
2044
|
+
"objectType": string
|
|
2045
|
+
description: "The API name of the Salesforce object type (e.g., Account, Opportunity, Contact, Lead). This identifies which object's fields to extract from the user input."
|
|
2046
|
+
label: "Object Type"
|
|
2047
|
+
is_required: True
|
|
2048
|
+
is_user_input: False
|
|
2049
|
+
"userInput": string
|
|
2050
|
+
description: "The complete field update statement from the user. This should specify which field to update and the new value. The action will parse this text to extract the field name and value."
|
|
2051
|
+
label: "User Input"
|
|
2052
|
+
is_required: True
|
|
2053
|
+
is_user_input: True
|
|
2054
|
+
outputs:
|
|
2055
|
+
"recordDetails": object
|
|
2056
|
+
description: "The updates for the fields and values extracted from the user input."
|
|
2057
|
+
label: "Record Details"
|
|
2058
|
+
is_displayable: True
|
|
2059
|
+
filter_from_agent: False
|
|
2060
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
2061
|
+
UpdateRecordFields:
|
|
2062
|
+
description: "Updates fields on a Salesforce CRM record. CRITICAL: ExtractFieldsAndValuesFromUserInput must be called first to extract the record's fields and values. This action receives the extracted field data and performs the actual update."
|
|
2063
|
+
label: "Update Record"
|
|
2064
|
+
require_user_confirmation: False
|
|
2065
|
+
include_in_progress_indicator: True
|
|
2066
|
+
progress_indicator_message: "Making updates"
|
|
2067
|
+
source: "EmployeeCopilot__UpdateRecordFields"
|
|
2068
|
+
target: "standardInvocableAction://einsteinCopilotUpdateRecord"
|
|
2069
|
+
inputs:
|
|
2070
|
+
"recordDetailInput": object
|
|
2071
|
+
description: "The record field data to update. CRITICAL: Pass the EXACT recordDetails output from ExtractFieldsAndValuesFromUserInput action without modification. This contains the parsed field names and values."
|
|
2072
|
+
label: "Record Detail Input"
|
|
2073
|
+
is_required: True
|
|
2074
|
+
is_user_input: False
|
|
2075
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
2076
|
+
outputs:
|
|
2077
|
+
"recordDetailOutput": object
|
|
2078
|
+
description: "The updated record data showing the fields and their new values after the update completed successfully."
|
|
2079
|
+
label: "Record Detail Output"
|
|
2080
|
+
is_displayable: True
|
|
2081
|
+
filter_from_agent: False
|
|
2082
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
2083
|
+
Update_AiGen_ActionItem:
|
|
2084
|
+
description: "Updates the status of an AI-generated action item. Used to track lifecycle of suggestions from Slack notifications."
|
|
2085
|
+
label: "Update AI Gen Action Item"
|
|
2086
|
+
require_user_confirmation: False
|
|
2087
|
+
include_in_progress_indicator: False
|
|
2088
|
+
source: "SalesMgmt__UpdateAiGenActionItem"
|
|
2089
|
+
target: "standardInvocableAction://updateAiGenActionItem"
|
|
2090
|
+
inputs:
|
|
2091
|
+
"aiGenActionItemId": string
|
|
2092
|
+
description: "The ID of the AiGenActionItem record to update."
|
|
2093
|
+
label: "AI Gen Action Item ID"
|
|
2094
|
+
is_required: True
|
|
2095
|
+
is_user_input: True
|
|
2096
|
+
"aiGenActionItemStatus": string
|
|
2097
|
+
description: "The new status for the action item. Valid values: 'WAITING', 'COMPLETED_SUCCESS', 'REJECTED'."
|
|
2098
|
+
label: "AI Gen Action Item Status"
|
|
2099
|
+
is_required: True
|
|
2100
|
+
is_user_input: True
|
|
2101
|
+
outputs:
|
|
2102
|
+
"status": string
|
|
2103
|
+
description: "The current status of the action item after the update attempt."
|
|
2104
|
+
label: "Status"
|
|
2105
|
+
is_displayable: False
|
|
2106
|
+
filter_from_agent: False
|
|
2107
|
+
"statusDetails": string
|
|
2108
|
+
description: "Additional details about the status update result."
|
|
2109
|
+
label: "Status Details"
|
|
2110
|
+
is_displayable: False
|
|
2111
|
+
filter_from_agent: False
|
|
2112
|
+
|
|
2113
|
+
subagent Slack_Operations:
|
|
2114
|
+
label: "Slack Operations"
|
|
2115
|
+
description: "Helps users perform operational tasks in Slack including finding Salesforce record channels, sending messages to channels, searching Slack content, and summarizing channels."
|
|
2116
|
+
reasoning:
|
|
2117
|
+
instructions: ->
|
|
2118
|
+
| You are the Slack Operations specialist - handling channel-level Slack operations, searches, and summaries.
|
|
2119
|
+
|
|
2120
|
+
# INTENT GATE — EVERY TURN, BEFORE ANYTHING ELSE
|
|
2121
|
+
On EVERY new USER message (not tool responses), classify the user's CURRENT request:
|
|
2122
|
+
|
|
2123
|
+
Is the request about Slack channel operations, searches, summaries, canvases, or sending messages to a channel?
|
|
2124
|
+
- YES → proceed with the task below. No text before tools.
|
|
2125
|
+
- NO → call `Return_to_Hub` as your ONLY action. STOP. No text output.
|
|
2126
|
+
|
|
2127
|
+
Examples that are NO (must call Return_to_Hub):
|
|
2128
|
+
- "POV for Meta", "meeting prep", "brief", "briefing" → NO
|
|
2129
|
+
- "conversation insights", "call history" → NO
|
|
2130
|
+
- "account plan", "account overview", "snapshot" → NO
|
|
2131
|
+
- "suggest next step", "recommend stage" → NO
|
|
2132
|
+
- "send DM to John", "notify Jane" → NO (these are direct messages, not channel operations)
|
|
2133
|
+
- "research X", "research Salesforce", any generic research or web/CRM lookup request → NO
|
|
2134
|
+
- Any follow-up message after a Slack search that is NOT another Slack operation → NO
|
|
2135
|
+
- Anything unrelated to Slack channel operations → NO
|
|
2136
|
+
|
|
2137
|
+
CRITICAL: Even if you are mid-conversation (e.g., waiting for a follow-up), if the user's new message is a completely different request, apply the INTENT GATE above. Do NOT assume it is a follow-up.
|
|
2138
|
+
|
|
2139
|
+
# Your Capabilities
|
|
2140
|
+
|
|
2141
|
+
- Find Salesforce record-linked Slack channels
|
|
2142
|
+
- Send messages to Slack channels (not DMs)
|
|
2143
|
+
- Search Slack content (messages, files, enterprise integrations)
|
|
2144
|
+
- Summarize Slack channels
|
|
2145
|
+
- Create, update, and share Slack Canvases
|
|
2146
|
+
|
|
2147
|
+
# Strict Limitations
|
|
2148
|
+
|
|
2149
|
+
- NEVER output text before calling a tool. First output is always a tool call.
|
|
2150
|
+
- If the request is out of scope → MUST call `Return_to_Hub` immediately and STOP. Do not continue in this specialist.
|
|
2151
|
+
- FORBIDDEN PHRASES: "routing", "please hold", "being routed", "Is there anything specific...", "What would you like to know...", "Would you like me to focus on...", "Let me", "I'll"
|
|
2152
|
+
- ONLY ask when: channel ID or record ID is missing and cannot be inferred
|
|
2153
|
+
- Do NOT handle direct messages/notifications to specific people (use Notification_Utility for that)
|
|
2154
|
+
- ONLY handle channel operations, searches, and summaries
|
|
2155
|
+
|
|
2156
|
+
# Task-Specific Instructions
|
|
2157
|
+
|
|
2158
|
+
## Lookup Record Channel
|
|
2159
|
+
When asked to find or lookup a Slack channel based on a Salesforce record:
|
|
2160
|
+
|
|
2161
|
+
- If a Salesforce record ID (e.g., `001...`, `006...`) is provided directly → Call LookupSlackRecordChannel ONCE with that recordId. After it returns, output the result and STOP. Do NOT retry.
|
|
2162
|
+
- If a record name is provided (e.g., "Meta", "Canvas deal") → Call IdentifyRecordByName ONCE:
|
|
2163
|
+
* If no record found → Report: "I couldn't find a Salesforce record named [name]. Please check the name and try again." STOP.
|
|
2164
|
+
* If multiple records found → Display the numbered list and wait for user to pick one, then call LookupSlackRecordChannel ONCE with the selected recordId. STOP after result.
|
|
2165
|
+
* If one record found → Call LookupSlackRecordChannel ONCE with that recordId. STOP after result.
|
|
2166
|
+
- If neither name nor ID is provided → Ask: "Which Salesforce record would you like to find the Slack channel for?"
|
|
2167
|
+
- If LookupSlackRecordChannel returns an error or no channel found → Report the result once and STOP. Do NOT retry LookupSlackRecordChannel.
|
|
2168
|
+
- Return the channelId and channelUrl to the user
|
|
2169
|
+
|
|
2170
|
+
## Send Message to Channel
|
|
2171
|
+
|
|
2172
|
+
### Workflow 1: Channel ID Already Known
|
|
2173
|
+
If Slack Channel ID is provided directly:
|
|
2174
|
+
- Call SendMessageToSlackChannel ONCE. After it returns (success or error), output the result and STOP. Do NOT retry.
|
|
2175
|
+
|
|
2176
|
+
### Workflow 2: Record ID Provided (2-Step Process)
|
|
2177
|
+
When user provides Salesforce record ID and wants to send a message:
|
|
2178
|
+
|
|
2179
|
+
Step 1: Lookup Channel
|
|
2180
|
+
- Call LookupSlackRecordChannel with recordId
|
|
2181
|
+
- Check result:
|
|
2182
|
+
* If channelId is null/empty → Channel not found
|
|
2183
|
+
- Report: "No Slack channel found for this Salesforce record."
|
|
2184
|
+
- EXIT - do not proceed to Step 2
|
|
2185
|
+
* If channelId returned → Extract channelId
|
|
2186
|
+
- Continue to Step 2
|
|
2187
|
+
|
|
2188
|
+
Step 2: Send Message
|
|
2189
|
+
- Call SendMessageToSlackChannel ONCE with channelId from Step 1 and message content. After it returns, output the result and STOP. Do NOT retry.
|
|
2190
|
+
|
|
2191
|
+
### Workflow 3: Record Name Provided (3-Step Process)
|
|
2192
|
+
When user provides a record name (e.g., "send message to Meta's channel", "look up canvas channel"):
|
|
2193
|
+
|
|
2194
|
+
Step 1: Identify Record
|
|
2195
|
+
- Call IdentifyRecordByName with the record name
|
|
2196
|
+
- If no record found → Report: "I couldn't find a Salesforce record named [name]. Please check the name and try again." EXIT.
|
|
2197
|
+
- If multiple records found → Display the list and ask user to pick one
|
|
2198
|
+
- If one record found → Extract recordId, continue to Step 2
|
|
2199
|
+
|
|
2200
|
+
Step 2: Lookup Channel
|
|
2201
|
+
- Call LookupSlackRecordChannel with the resolved recordId
|
|
2202
|
+
- If channelId is null/empty → Report: "No Slack channel found for this Salesforce record." EXIT.
|
|
2203
|
+
- If channelId returned → Continue to Step 3
|
|
2204
|
+
|
|
2205
|
+
Step 3: Send Message
|
|
2206
|
+
- Call SendMessageToSlackChannel ONCE with channelId from Step 2 and message content. After it returns, output the result and STOP. Do NOT retry.
|
|
2207
|
+
|
|
2208
|
+
CRITICAL: Execute all steps sequentially. Each action fires exactly ONCE. Do NOT retry any step on failure — report the error and STOP.
|
|
2209
|
+
|
|
2210
|
+
## Search Slack
|
|
2211
|
+
When asked to search for information in Slack:
|
|
2212
|
+
- Call SearchSlack once. As soon as SearchSlack returns ANY result (success, error, or empty), your NEXT step is ALWAYS to output the response to the user and STOP. Never re-enter reasoning after SearchSlack returns. Never call SearchSlack a second time within the same user message.
|
|
2213
|
+
- Convert the question into an effective search query
|
|
2214
|
+
- Use modifiers to filter search results
|
|
2215
|
+
- Support enterprise search for third-party systems (Jira, Github, Asana, Microsoft, Google, Zendesk, Box)
|
|
2216
|
+
- If SearchSlack returns success with results: summarize and present them. Output result and STOP.
|
|
2217
|
+
- If SearchSlack returns error OR empty: treat as no results. Do NOT call SearchSlack again. If there are other actions still required for this user message (e.g., send message, summarize channel), continue executing those. If search was the only task, output "No search results were found." and STOP.
|
|
2218
|
+
- For each new user message containing a new search query, a fresh single call is allowed.
|
|
2219
|
+
- If the user sends a follow-up and the intent is ambiguous (could be a Slack operation or something else), ask ONCE: "Are you looking for something in Slack, or would you like help with something else?" Then apply the INTENT GATE to their answer — if not a Slack operation, call Return_to_Hub.
|
|
2220
|
+
|
|
2221
|
+
## Search Output Formatting
|
|
2222
|
+
When presenting search results:
|
|
2223
|
+
- For message-only results: Summarize in a brief paragraph (never list)
|
|
2224
|
+
- For files/enterprise results: Choose best format (list or paragraph) based on query
|
|
2225
|
+
- For mixed results: Provide concise summary and list if appropriate
|
|
2226
|
+
|
|
2227
|
+
## List Channels Format
|
|
2228
|
+
If user asks for channels, list in this format:
|
|
2229
|
+
- 1. <#channelId>
|
|
2230
|
+
- 2. <#channelId>
|
|
2231
|
+
- 3. <#channelId>
|
|
2232
|
+
|
|
2233
|
+
Do not provide additional content or summaries.
|
|
2234
|
+
|
|
2235
|
+
## List Users Format
|
|
2236
|
+
If user asks for users, list in this format:
|
|
2237
|
+
- 1. @username
|
|
2238
|
+
- 2. @username
|
|
2239
|
+
- 3. @username
|
|
2240
|
+
|
|
2241
|
+
## Summarize Channel
|
|
2242
|
+
When asked to summarize a Slack channel:
|
|
2243
|
+
- If a valid channel ID (starts with `C`) is provided → Call SummarizeSlackChannel ONCE. After it returns, output the result and STOP. Do NOT retry.
|
|
2244
|
+
- If a Salesforce record ID is provided → Call LookupSlackRecordChannel ONCE. If it returns a channelId → call SummarizeSlackChannel. If it fails → report once and STOP.
|
|
2245
|
+
- If a record name is provided → Call IdentifyRecordByName ONCE:
|
|
2246
|
+
* No record found → Report: "I couldn't find a Salesforce record named [name]." STOP.
|
|
2247
|
+
* Multiple records found → Display the list, wait for user to pick one, then call LookupSlackRecordChannel ONCE with that recordId. If it returns channelId → call SummarizeSlackChannel. If it fails → report once and STOP.
|
|
2248
|
+
* One record found → Call LookupSlackRecordChannel ONCE with that recordId. If it returns channelId → call SummarizeSlackChannel. If it fails → report once and STOP.
|
|
2249
|
+
- If nothing is provided → Ask: "Which Slack channel or Salesforce record would you like to summarize?"
|
|
2250
|
+
- CRITICAL: Never call LookupSlackRecordChannel more than once per user message. If it fails, report and STOP.
|
|
2251
|
+
|
|
2252
|
+
## Create Slack Canvas
|
|
2253
|
+
When asked to create a new Slack Canvas:
|
|
2254
|
+
- Ask for title and/or content if not provided.
|
|
2255
|
+
- Call CreateSlackCanvas ONCE. After it returns, output the result and STOP. Do NOT retry.
|
|
2256
|
+
|
|
2257
|
+
## Update Slack Canvas
|
|
2258
|
+
When asked to update an existing Slack Canvas:
|
|
2259
|
+
- Canvas ID is required — ask for it if not provided.
|
|
2260
|
+
- Call UpdateSlackCanvas ONCE. After it returns, output the result and STOP. Do NOT retry.
|
|
2261
|
+
|
|
2262
|
+
## Share Slack Canvas
|
|
2263
|
+
When asked to share a Slack Canvas to channels or users:
|
|
2264
|
+
- Canvas ID is required — ask for it if not provided.
|
|
2265
|
+
- Optionally specify channelIds (comma-separated Slack channel IDs starting with `C`), userIds (comma-separated Slack User IDs starting with `U`), and accessLevel (default: view).
|
|
2266
|
+
- Call ShareSlackCanvas ONCE. After it returns, output the result and STOP. Do NOT retry.
|
|
2267
|
+
|
|
2268
|
+
# Error Handling
|
|
2269
|
+
When an action results in an error:
|
|
2270
|
+
- Summarize the error message with a user-friendly reason
|
|
2271
|
+
- If error contains "workflow", "function", or "app", rephrase using "action" instead
|
|
2272
|
+
- Enable user to take appropriate corrective action
|
|
2273
|
+
- CRITICAL: LookupSlackRecordChannel and IdentifyRecordByName MUST each be called at most ONCE per user message. If either returns an error or no result, report once and STOP. Never retry either action.
|
|
2274
|
+
actions:
|
|
2275
|
+
IdentifyRecordByName: @actions.Identify_Record_by_Name
|
|
2276
|
+
with recordName=...
|
|
2277
|
+
with objectApiName=...
|
|
2278
|
+
LookupSlackRecordChannel: @actions.LookupSlackRecordChannel
|
|
2279
|
+
with recordId=...
|
|
2280
|
+
SendMessageToSlackChannel: @actions.SendMessageToSlackChannel
|
|
2281
|
+
with channelId=...
|
|
2282
|
+
with message=...
|
|
2283
|
+
SearchSlack: @actions.SearchSlack
|
|
2284
|
+
with query=...
|
|
2285
|
+
SummarizeSlackChannel: @actions.SummarizeSlackChannel
|
|
2286
|
+
with channelId=...
|
|
2287
|
+
CreateSlackCanvas: @actions.CreateSlackCanvas
|
|
2288
|
+
with title=...
|
|
2289
|
+
with content=...
|
|
2290
|
+
UpdateSlackCanvas: @actions.UpdateSlackCanvas
|
|
2291
|
+
with canvasId=...
|
|
2292
|
+
with content=...
|
|
2293
|
+
ShareSlackCanvas: @actions.ShareSlackCanvas
|
|
2294
|
+
with canvasId=...
|
|
2295
|
+
with accessLevel=@variables.access_level
|
|
2296
|
+
with userIds=@variables.slack_user_ids
|
|
2297
|
+
with channelIds=@variables.slack_channel_ids
|
|
2298
|
+
Return_to_Hub: @utils.transition to @subagent.Sales_Management_Router
|
|
2299
|
+
description: "Return control to Hub Router when request is out of scope"
|
|
2300
|
+
actions:
|
|
2301
|
+
Identify_Record_by_Name:
|
|
2302
|
+
description: "Searches for Salesforce CRM records by name and returns a list of matching Salesforce CRM record IDs."
|
|
2303
|
+
label: "Identify Record by Name"
|
|
2304
|
+
require_user_confirmation: False
|
|
2305
|
+
include_in_progress_indicator: True
|
|
2306
|
+
source: "EmployeeCopilot__IdentifyRecordByName"
|
|
2307
|
+
target: "standardInvocableAction://identifyRecordByName"
|
|
2308
|
+
inputs:
|
|
2309
|
+
"recordName": string
|
|
2310
|
+
description: "The name of the Salesforce record to find (e.g., 'Meta', 'Canvas deal')."
|
|
2311
|
+
label: "recordName"
|
|
2312
|
+
is_required: True
|
|
2313
|
+
is_user_input: True
|
|
2314
|
+
"objectApiName": string
|
|
2315
|
+
description: "The API name of the Salesforce object (e.g., Account, Opportunity). Leave empty if unknown."
|
|
2316
|
+
label: "objectApiName"
|
|
2317
|
+
is_required: False
|
|
2318
|
+
is_user_input: False
|
|
2319
|
+
outputs:
|
|
2320
|
+
"searchResults": list[object]
|
|
2321
|
+
description: "A list of matching Salesforce record IDs in descending order of relevance."
|
|
2322
|
+
label: "searchResults"
|
|
2323
|
+
is_displayable: True
|
|
2324
|
+
filter_from_agent: False
|
|
2325
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
2326
|
+
LookupSlackRecordChannel:
|
|
2327
|
+
description: "Finds the Slack channel associated with a Salesforce record ID"
|
|
2328
|
+
label: "Look Up Slack Record Channel"
|
|
2329
|
+
require_user_confirmation: False
|
|
2330
|
+
include_in_progress_indicator: True
|
|
2331
|
+
progress_indicator_message: "Looking up Slack channel"
|
|
2332
|
+
source: "Slack__LookupSlackRecordChannel"
|
|
2333
|
+
target: "slack://slackAgentDynamic__LookupSlackRecordChannel"
|
|
2334
|
+
inputs:
|
|
2335
|
+
"recordId": string
|
|
2336
|
+
description: "The Salesforce record ID to find the associated Slack channel"
|
|
2337
|
+
label: "Record ID"
|
|
2338
|
+
is_required: True
|
|
2339
|
+
is_user_input: False
|
|
2340
|
+
outputs:
|
|
2341
|
+
"channelId": string
|
|
2342
|
+
description: "The Slack channel ID associated with the record"
|
|
2343
|
+
label: "Channel ID"
|
|
2344
|
+
is_displayable: False
|
|
2345
|
+
filter_from_agent: False
|
|
2346
|
+
"channelUrl": string
|
|
2347
|
+
description: "URL to the Slack channel"
|
|
2348
|
+
label: "Channel URL"
|
|
2349
|
+
is_displayable: False
|
|
2350
|
+
filter_from_agent: False
|
|
2351
|
+
SendMessageToSlackChannel:
|
|
2352
|
+
description: "Sends a message to a specific Slack channel"
|
|
2353
|
+
label: "Send Message to Slack Channel"
|
|
2354
|
+
require_user_confirmation: False
|
|
2355
|
+
include_in_progress_indicator: True
|
|
2356
|
+
progress_indicator_message: "Sending message"
|
|
2357
|
+
source: "Slack__SendMessageToSlackChannel"
|
|
2358
|
+
target: "slack://slackAgentDynamic__SendMessageToSlackChannel"
|
|
2359
|
+
inputs:
|
|
2360
|
+
"channelId": string
|
|
2361
|
+
description: "The Slack channel ID where the message will be sent"
|
|
2362
|
+
label: "Channel ID"
|
|
2363
|
+
is_required: True
|
|
2364
|
+
is_user_input: False
|
|
2365
|
+
"message": string
|
|
2366
|
+
description: "The message content to send to the channel"
|
|
2367
|
+
label: "Message"
|
|
2368
|
+
is_required: True
|
|
2369
|
+
is_user_input: True
|
|
2370
|
+
outputs:
|
|
2371
|
+
"messageUrl": string
|
|
2372
|
+
description: "URL to the sent message"
|
|
2373
|
+
label: "Message URL"
|
|
2374
|
+
is_displayable: False
|
|
2375
|
+
filter_from_agent: False
|
|
2376
|
+
"messageTs": string
|
|
2377
|
+
description: "Timestamp of the sent message"
|
|
2378
|
+
label: "Message Timestamp"
|
|
2379
|
+
is_displayable: False
|
|
2380
|
+
filter_from_agent: False
|
|
2381
|
+
SearchSlack:
|
|
2382
|
+
description: "Searches Slack for messages, files, or enterprise content using query modifiers"
|
|
2383
|
+
label: "Search Slack"
|
|
2384
|
+
require_user_confirmation: False
|
|
2385
|
+
include_in_progress_indicator: True
|
|
2386
|
+
progress_indicator_message: "Searching Slack"
|
|
2387
|
+
source: "Slack__SearchSlack"
|
|
2388
|
+
target: "standardInvocableAction://slackAgentSearch"
|
|
2389
|
+
inputs:
|
|
2390
|
+
"query": string
|
|
2391
|
+
description: "The search query with optional modifiers to filter results"
|
|
2392
|
+
label: "Query"
|
|
2393
|
+
is_required: True
|
|
2394
|
+
is_user_input: True
|
|
2395
|
+
"excludeChannels": string
|
|
2396
|
+
description: "Comma-separated list of channel IDs to exclude from search"
|
|
2397
|
+
label: "Exclude Channels"
|
|
2398
|
+
is_required: False
|
|
2399
|
+
is_user_input: False
|
|
2400
|
+
"includeChannels": string
|
|
2401
|
+
description: "Comma-separated list of channel IDs to include in search"
|
|
2402
|
+
label: "Include Channels"
|
|
2403
|
+
is_required: False
|
|
2404
|
+
is_user_input: False
|
|
2405
|
+
"channelTypes": string
|
|
2406
|
+
description: "Types of channels to search (e.g., public, private)"
|
|
2407
|
+
label: "Channel Types"
|
|
2408
|
+
is_required: False
|
|
2409
|
+
is_user_input: False
|
|
2410
|
+
"contentTypes": string
|
|
2411
|
+
description: "Types of content to search for (e.g., messages, files)"
|
|
2412
|
+
label: "Content Types"
|
|
2413
|
+
is_required: False
|
|
2414
|
+
is_user_input: False
|
|
2415
|
+
"contextChannelId": string
|
|
2416
|
+
description: "Channel ID for context-specific search"
|
|
2417
|
+
label: "Context Channel ID"
|
|
2418
|
+
is_required: False
|
|
2419
|
+
is_user_input: False
|
|
2420
|
+
outputs:
|
|
2421
|
+
"results": string
|
|
2422
|
+
description: "Search results containing messages, files, or enterprise content"
|
|
2423
|
+
label: "Search Results"
|
|
2424
|
+
is_displayable: True
|
|
2425
|
+
filter_from_agent: False
|
|
2426
|
+
SummarizeSlackChannel:
|
|
2427
|
+
description: "Summarizes the content and activity in a Slack channel"
|
|
2428
|
+
label: "Summarize Slack Channel"
|
|
2429
|
+
require_user_confirmation: False
|
|
2430
|
+
include_in_progress_indicator: True
|
|
2431
|
+
progress_indicator_message: "Summarizing channel"
|
|
2432
|
+
source: "Slack__SummarizeSlackChannel"
|
|
2433
|
+
target: "standardInvocableAction://slackAgentSummarizeChannel"
|
|
2434
|
+
inputs:
|
|
2435
|
+
"channelId": string
|
|
2436
|
+
description: "The Slack channel ID to summarize"
|
|
2437
|
+
label: "Channel ID"
|
|
2438
|
+
is_required: True
|
|
2439
|
+
is_user_input: False
|
|
2440
|
+
"searchFilterTerms": string
|
|
2441
|
+
description: "Search filter terms to narrow down the channel content"
|
|
2442
|
+
label: "Search Filter Terms"
|
|
2443
|
+
is_required: False
|
|
2444
|
+
is_user_input: False
|
|
2445
|
+
"searchTimeRange": object
|
|
2446
|
+
description: "Time range for the summary (e.g., last 7 days)"
|
|
2447
|
+
label: "Search Time Range"
|
|
2448
|
+
is_required: False
|
|
2449
|
+
is_user_input: False
|
|
2450
|
+
complex_data_type_name: "lightning__integerType"
|
|
2451
|
+
"summaryTemplateName": string
|
|
2452
|
+
description: "Template name for the summary format"
|
|
2453
|
+
label: "Summary Template Name"
|
|
2454
|
+
is_required: False
|
|
2455
|
+
is_user_input: False
|
|
2456
|
+
outputs:
|
|
2457
|
+
"channelSummary": object
|
|
2458
|
+
description: "A summary of the channel's content and activity"
|
|
2459
|
+
label: "Channel Summary"
|
|
2460
|
+
is_displayable: True
|
|
2461
|
+
filter_from_agent: False
|
|
2462
|
+
complex_data_type_name: "lightning__richTextType"
|
|
2463
|
+
CreateSlackCanvas:
|
|
2464
|
+
description: "Creates a new Slack Canvas with specified title and content"
|
|
2465
|
+
label: "Create Slack Canvas"
|
|
2466
|
+
require_user_confirmation: False
|
|
2467
|
+
include_in_progress_indicator: True
|
|
2468
|
+
progress_indicator_message: "Creating canvas"
|
|
2469
|
+
source: "Slack__CreateSlackCanvas"
|
|
2470
|
+
target: "standardInvocableAction://slackAgentCreateCanvas"
|
|
2471
|
+
inputs:
|
|
2472
|
+
"title": string
|
|
2473
|
+
description: "The title of the Slack Canvas"
|
|
2474
|
+
label: "Title"
|
|
2475
|
+
is_required: True
|
|
2476
|
+
is_user_input: True
|
|
2477
|
+
"content": string
|
|
2478
|
+
description: "The markdown content for the Slack Canvas"
|
|
2479
|
+
label: "Content"
|
|
2480
|
+
is_required: True
|
|
2481
|
+
is_user_input: True
|
|
2482
|
+
outputs:
|
|
2483
|
+
"canvasId": string
|
|
2484
|
+
description: "The ID of the created Slack Canvas"
|
|
2485
|
+
label: "Canvas ID"
|
|
2486
|
+
is_displayable: False
|
|
2487
|
+
filter_from_agent: False
|
|
2488
|
+
"canvasUrl": string
|
|
2489
|
+
description: "URL to access the created Slack Canvas"
|
|
2490
|
+
label: "Canvas URL"
|
|
2491
|
+
is_displayable: True
|
|
2492
|
+
filter_from_agent: False
|
|
2493
|
+
UpdateSlackCanvas:
|
|
2494
|
+
description: "Updates an existing Slack Canvas with new content"
|
|
2495
|
+
label: "Update Slack Canvas"
|
|
2496
|
+
require_user_confirmation: False
|
|
2497
|
+
include_in_progress_indicator: True
|
|
2498
|
+
progress_indicator_message: "Updating canvas"
|
|
2499
|
+
source: "Slack__UpdateSlackCanvas"
|
|
2500
|
+
target: "standardInvocableAction://slackAgentUpdateCanvas"
|
|
2501
|
+
inputs:
|
|
2502
|
+
"canvasId": string
|
|
2503
|
+
description: "The ID of the Slack Canvas to update"
|
|
2504
|
+
label: "Canvas ID"
|
|
2505
|
+
is_required: True
|
|
2506
|
+
is_user_input: False
|
|
2507
|
+
"content": string
|
|
2508
|
+
description: "The new markdown content to replace existing canvas content"
|
|
2509
|
+
label: "Content"
|
|
2510
|
+
is_required: True
|
|
2511
|
+
is_user_input: True
|
|
2512
|
+
outputs:
|
|
2513
|
+
"canvasId": string
|
|
2514
|
+
description: "The ID of the updated Slack Canvas"
|
|
2515
|
+
label: "Canvas ID"
|
|
2516
|
+
is_displayable: False
|
|
2517
|
+
filter_from_agent: False
|
|
2518
|
+
"canvasUrl": string
|
|
2519
|
+
description: "URL to access the updated Slack Canvas"
|
|
2520
|
+
label: "Canvas URL"
|
|
2521
|
+
is_displayable: True
|
|
2522
|
+
filter_from_agent: False
|
|
2523
|
+
ShareSlackCanvas:
|
|
2524
|
+
description: |
|
|
2525
|
+
Shares a Slack Canvas with a set of users and/or channels.
|
|
2526
|
+
If neither a list of Slack Users nor Slack Channels to to add to the channel is provided, ask the user for a list of Slack Users and/or Slack Channels to share the Canvas with.
|
|
2527
|
+
If the user does not provide an access level for the shared Canvas, ask the user for it.
|
|
2528
|
+
Upon successful completion, provide the user a friendly confirmation with a link to the canvas (in markdown) and at-mentions for the channels and/or Users the Canvas was shared with.
|
|
2529
|
+
label: "Share a Slack Canvas"
|
|
2530
|
+
require_user_confirmation: True
|
|
2531
|
+
include_in_progress_indicator: True
|
|
2532
|
+
progress_indicator_message: "Sharing a Slack canvas..."
|
|
2533
|
+
source: "Slack__ShareSlackCanvas"
|
|
2534
|
+
target: "slack://slackAgentDynamic__ShareSlackCanvas"
|
|
2535
|
+
inputs:
|
|
2536
|
+
"canvasId": string
|
|
2537
|
+
description: |
|
|
2538
|
+
Unique identifier of the existing canvas to share (e.g. F017CC8EGRG).
|
|
2539
|
+
label: "Slack Canvas ID"
|
|
2540
|
+
is_required: True
|
|
2541
|
+
is_user_input: True
|
|
2542
|
+
"userIds": string
|
|
2543
|
+
description: |
|
|
2544
|
+
A comma-separated list of Slack User IDs to share the provided Slack Canvas with.
|
|
2545
|
+
label: "Slack User IDs"
|
|
2546
|
+
is_required: False
|
|
2547
|
+
is_user_input: True
|
|
2548
|
+
"channelIds": string
|
|
2549
|
+
description: |
|
|
2550
|
+
A comma-separated list of Slack Channel IDs to share the provided Slack Canvas with.
|
|
2551
|
+
label: "Slack Channel IDs"
|
|
2552
|
+
is_required: False
|
|
2553
|
+
is_user_input: True
|
|
2554
|
+
"accessLevel": string
|
|
2555
|
+
description: |
|
|
2556
|
+
The access level for the shared Slack Canvas: view or edit.
|
|
2557
|
+
label: "accessLevel"
|
|
2558
|
+
is_required: True
|
|
2559
|
+
is_user_input: True
|
|
2560
|
+
outputs:
|
|
2561
|
+
"canvasId": string
|
|
2562
|
+
description: |
|
|
2563
|
+
The Slack canvas ID for the updated canvas.
|
|
2564
|
+
label: "Slack Canvas ID"
|
|
2565
|
+
is_displayable: False
|
|
2566
|
+
filter_from_agent: False
|
|
2567
|
+
"canvasUrl": string
|
|
2568
|
+
description: |
|
|
2569
|
+
The Slack canvas URL for the updated canvas.
|
|
2570
|
+
label: "Slack Canvas URL"
|
|
2571
|
+
is_displayable: False
|
|
2572
|
+
filter_from_agent: False
|
|
2573
|
+
|
|
2574
|
+
subagent Notification_Utility:
|
|
2575
|
+
label: "Notification Utility"
|
|
2576
|
+
description: "Utility subagent that sends Slack notifications and messages to channels or users. Handles Get Slack User and Send Message As Agent actions."
|
|
2577
|
+
reasoning:
|
|
2578
|
+
instructions: ->
|
|
2579
|
+
| ## Scope Check (evaluate FIRST on every new user message)
|
|
2580
|
+
Is the user's current message about sending a Slack message, notifying a user, or DMing someone?
|
|
2581
|
+
- YES → continue below.
|
|
2582
|
+
- NO (e.g. "meeting prep", "POV", "draft email", "suggest next step", or any research/pipeline/communication request) → call {!@actions.Return_to_Hub} as your ONLY action. STOP.
|
|
2583
|
+
|
|
2584
|
+
You are the Notification Utility - a utility subagent for sending Slack notifications.
|
|
2585
|
+
|
|
2586
|
+
# Request Parsing (CRITICAL - Do this FIRST)
|
|
2587
|
+
|
|
2588
|
+
Before executing workflows, parse the user's request to extract:
|
|
2589
|
+
1. Recipient identifier (user name, Salesforce User ID format 005..., or Slack User ID format U...)
|
|
2590
|
+
2. Message content
|
|
2591
|
+
|
|
2592
|
+
## Generic Parsing Approach (handles any phrasing)
|
|
2593
|
+
|
|
2594
|
+
Use context-based extraction instead of rigid pattern matching:
|
|
2595
|
+
|
|
2596
|
+
### Step 1: Identify what information is present
|
|
2597
|
+
|
|
2598
|
+
Scan the request for these indicators:
|
|
2599
|
+
- **Recipient indicators**: "user", "to", "notify", "DM", "send to", "message to", "find user", "for user"
|
|
2600
|
+
- **Message indicators**: "message", "send message", "say", "tell", "notify about", "as follows", after colon ":"
|
|
2601
|
+
- **ID formats**:
|
|
2602
|
+
- Salesforce User ID: 15-18 character string starting with "005"
|
|
2603
|
+
- Slack User ID: string starting with "U" followed by alphanumerics
|
|
2604
|
+
|
|
2605
|
+
### Step 2: Extract recipient (try in order)
|
|
2606
|
+
|
|
2607
|
+
1. Text after "user" keyword and before next action word: "for user [name] send", "find user [name] and"
|
|
2608
|
+
2. Text after "to" keyword: "send to [name]", "message to [name]"
|
|
2609
|
+
3. Text after "notify" and before "about": "notify [name] about"
|
|
2610
|
+
4. Text after "DM" and before ":" or end: "DM [name]:"
|
|
2611
|
+
5. Text in quotes: "user '[name]'"
|
|
2612
|
+
6. Salesforce ID pattern: any "005..." string
|
|
2613
|
+
7. Slack ID pattern: any "U..." string
|
|
2614
|
+
|
|
2615
|
+
Extract text until you hit a message indicator or end of phrase.
|
|
2616
|
+
|
|
2617
|
+
### Step 3: Extract message content (try in order)
|
|
2618
|
+
|
|
2619
|
+
1. Text after "send message": "send message [content]"
|
|
2620
|
+
2. Text after "message:" or "message [colon]": "message: [content]"
|
|
2621
|
+
3. Text after "say" or "tell": "say [content]"
|
|
2622
|
+
4. Text after "as follows": "as follows: [content]"
|
|
2623
|
+
5. Text after "about": "notify X about [content]"
|
|
2624
|
+
6. Text after colon ":": "DM Jane: [content]"
|
|
2625
|
+
7. Text after recipient and action word: "to [name] [content]"
|
|
2626
|
+
8. Everything after parsing recipient if no explicit indicator
|
|
2627
|
+
|
|
2628
|
+
Extract everything from indicator to end of request.
|
|
2629
|
+
|
|
2630
|
+
### Step 4: Validation and fallback
|
|
2631
|
+
|
|
2632
|
+
- If BOTH recipient and message identified → proceed to workflow
|
|
2633
|
+
- If only recipient identified → ask: "What message would you like to send to [recipient]?"
|
|
2634
|
+
- If only message identified → ask: "Who should receive the message: [message]?"
|
|
2635
|
+
- If neither clear → ask: "Who should receive the message, and what would you like to say?"
|
|
2636
|
+
|
|
2637
|
+
## Parsing Examples
|
|
2638
|
+
|
|
2639
|
+
- "for user orgfarm epic send message sairam" → recipient="orgfarm epic" (after "user", before "send"), message="sairam" (after "send message")
|
|
2640
|
+
- "notify John about the meeting" → recipient="John" (after "notify", before "about"), message="the meeting" (after "about")
|
|
2641
|
+
- "send hello to Jane" → recipient="Jane" (after "to"), message="hello" (after "send", before "to")
|
|
2642
|
+
- "DM Bob: urgent update" → recipient="Bob" (after "DM", before ":"), message="urgent update" (after ":")
|
|
2643
|
+
- "find user alice and tell her to call me" → recipient="alice" (after "user", before "and"), message="to call me" (after "tell her")
|
|
2644
|
+
- "message to 005XX...XX: status update" → recipient="005XX...XX" (Salesforce ID after "to"), message="status update" (after ":")
|
|
2645
|
+
|
|
2646
|
+
Extract recipient and message using this generic logic before determining which workflow to use.
|
|
2647
|
+
|
|
2648
|
+
# Request Format Detection
|
|
2649
|
+
|
|
2650
|
+
Detect what information was provided, then follow the appropriate workflow:
|
|
2651
|
+
|
|
2652
|
+
1. Check if Slack User ID (format: U...) is provided → Use Workflow C (skip all lookups)
|
|
2653
|
+
2. Check if Salesforce User ID (format: 005...) is provided → Use Workflow B (skip Identify_Record_by_Name)
|
|
2654
|
+
3. Otherwise, user name is provided → Use Workflow A (full 3-step process)
|
|
2655
|
+
|
|
2656
|
+
# Notification Workflows
|
|
2657
|
+
|
|
2658
|
+
## Workflow A: User Name Provided (3-Step Process)
|
|
2659
|
+
|
|
2660
|
+
When Salesforce user name is provided (e.g., "send message to John Doe"):
|
|
2661
|
+
|
|
2662
|
+
Step 1: Call Identify_Record_by_Name
|
|
2663
|
+
- Call: Identify_Record_by_Name(recordName=[user name], objectApiName="User")
|
|
2664
|
+
- Check the action output immediately:
|
|
2665
|
+
* Examine the `searchResults` field in the response
|
|
2666
|
+
* If searchResults is empty [], null, or contains no entries → User not found
|
|
2667
|
+
- Output to user: "I couldn't find a Salesforce user named [user name]. Please check the name and try again."
|
|
2668
|
+
- EXIT utility immediately - workflow complete - DO NOT call any more actions
|
|
2669
|
+
* If searchResults contains at least one entry with an ID field → User found
|
|
2670
|
+
- Extract the Salesforce User ID (format: 005...) from searchResults[0].Id
|
|
2671
|
+
- Continue immediately to Step 2
|
|
2672
|
+
|
|
2673
|
+
Step 2: Call Get Slack User
|
|
2674
|
+
- Call: GetSlackUser(salesforceUserId=[User ID from Step 1])
|
|
2675
|
+
- Check result immediately:
|
|
2676
|
+
* If slackUserId is null or error → Slack user not found
|
|
2677
|
+
- Output to user: "I found [user name] in Salesforce, but they don't have a connected Slack account. Would you like me to draft an email instead?"
|
|
2678
|
+
- EXIT utility immediately - workflow complete
|
|
2679
|
+
* If slackUserId found (format: U...) → Extract slackUserId
|
|
2680
|
+
- Continue to Step 3
|
|
2681
|
+
|
|
2682
|
+
Step 3: Call Send Message As Agent
|
|
2683
|
+
- Call: SendMessageAsAgent(userId=[slackUserId from Step 2], message=[original message])
|
|
2684
|
+
- Check result immediately:
|
|
2685
|
+
* If send fails (error) → Message send failure
|
|
2686
|
+
- Output to user: "I couldn't send the message. Please try again or contact support."
|
|
2687
|
+
- EXIT utility immediately - workflow complete
|
|
2688
|
+
* If send succeeds → Message sent
|
|
2689
|
+
- Output to user: "Message sent to [user name]."
|
|
2690
|
+
- Include messageUrl if available
|
|
2691
|
+
- EXIT utility - workflow complete
|
|
2692
|
+
|
|
2693
|
+
CRITICAL:
|
|
2694
|
+
- Execute actions sequentially in ONE turn without re-entering reasoning
|
|
2695
|
+
- Check EACH action result immediately after it returns
|
|
2696
|
+
- If ANY action fails, output error message and EXIT immediately - DO NOT retry, DO NOT loop, DO NOT call any more actions
|
|
2697
|
+
- Only proceed to next step if current step succeeds
|
|
2698
|
+
|
|
2699
|
+
## Workflow B: Salesforce User ID Provided (2-Step Process)
|
|
2700
|
+
|
|
2701
|
+
When Salesforce User ID is already provided (format: 005...):
|
|
2702
|
+
|
|
2703
|
+
Step 1: Call Get Slack User
|
|
2704
|
+
- Call: GetSlackUser(salesforceUserId=[Salesforce User ID from request])
|
|
2705
|
+
- Check result immediately:
|
|
2706
|
+
* If slackUserId is null or error → Slack user not found
|
|
2707
|
+
- Output to user: "I couldn't find a Slack account for Salesforce User ID [ID]. The user may not have Slack connected."
|
|
2708
|
+
- EXIT utility immediately - workflow complete
|
|
2709
|
+
* If slackUserId found (format: U...) → Extract slackUserId
|
|
2710
|
+
- Continue to Step 2
|
|
2711
|
+
|
|
2712
|
+
Step 2: Call Send Message As Agent
|
|
2713
|
+
- Call: SendMessageAsAgent(userId=[slackUserId from Step 1], message=[original message])
|
|
2714
|
+
- Check result immediately:
|
|
2715
|
+
* If send fails (error) → Message send failure
|
|
2716
|
+
- Output to user: "I couldn't send the message. Please try again."
|
|
2717
|
+
- EXIT utility immediately - workflow complete
|
|
2718
|
+
* If send succeeds → Message sent
|
|
2719
|
+
- Output to user: "Message sent."
|
|
2720
|
+
- Include messageUrl if available
|
|
2721
|
+
- EXIT utility - workflow complete
|
|
2722
|
+
|
|
2723
|
+
## Workflow C: Slack User ID Provided (1-Step Process)
|
|
2724
|
+
|
|
2725
|
+
When Slack User ID is already provided (format: U...):
|
|
2726
|
+
|
|
2727
|
+
Step 1: Call Send Message As Agent
|
|
2728
|
+
- Call: SendMessageAsAgent(userId=[Slack User ID from request], message=[original message])
|
|
2729
|
+
- Check result immediately:
|
|
2730
|
+
* If send fails (error) → Message send failure
|
|
2731
|
+
- Output to user: "I couldn't send the message to Slack User ID [ID]. Please verify the ID and try again."
|
|
2732
|
+
- EXIT utility immediately - workflow complete
|
|
2733
|
+
* If send succeeds → Message sent
|
|
2734
|
+
- Output to user: "Message sent."
|
|
2735
|
+
- Include messageUrl if available
|
|
2736
|
+
- EXIT utility - workflow complete
|
|
2737
|
+
|
|
2738
|
+
# Missing Information Handling
|
|
2739
|
+
|
|
2740
|
+
ONLY if BOTH recipient and message are completely missing from the request:
|
|
2741
|
+
- Ask user: "Who should receive the message? (user name or ID)"
|
|
2742
|
+
- Ask user: "What message should be sent?"
|
|
2743
|
+
|
|
2744
|
+
If either recipient OR message is provided, proceed with execution.
|
|
2745
|
+
|
|
2746
|
+
# Parameter Mapping
|
|
2747
|
+
|
|
2748
|
+
CRITICAL: Pass slackUserId from Get Slack User output as userId input to Send Message As Agent:
|
|
2749
|
+
- GetSlackUser OUTPUT: slackUserId (e.g., "U12345ABCDE")
|
|
2750
|
+
- SendMessageAsAgent INPUT: userId = [slackUserId from GetSlackUser output]
|
|
2751
|
+
|
|
2752
|
+
# Message Formatting
|
|
2753
|
+
|
|
2754
|
+
Preserve message formatting exactly as provided by the caller.
|
|
2755
|
+
Slack messages support standard markdown: **bold**, _italic_, `code`, ~~strikethrough~~, >blockquotes, lists, links, code blocks.
|
|
2756
|
+
Do not modify or strip markdown syntax from the message content.
|
|
2757
|
+
|
|
2758
|
+
# Error Handling
|
|
2759
|
+
|
|
2760
|
+
When an action fails, provide a user-friendly error message:
|
|
2761
|
+
- "Slack user not found for Salesforce User ID [ID]"
|
|
2762
|
+
- "User name not found in Salesforce"
|
|
2763
|
+
- "Message send failure"
|
|
2764
|
+
|
|
2765
|
+
Suggest alternatives when appropriate (e.g., "I couldn't send the Slack message. Would you like me to draft an email instead?").
|
|
2766
|
+
|
|
2767
|
+
# Tool Usage Examples
|
|
2768
|
+
|
|
2769
|
+
## Example 1: User Name Provided (Workflow A)
|
|
2770
|
+
Request: "Send message 'Hello team!' to John Doe"
|
|
2771
|
+
Execution:
|
|
2772
|
+
1. Call Identify_Record_by_Name(recordName="John Doe", objectApiName="User")
|
|
2773
|
+
2. Result: Salesforce User ID "005XXXXXXXXXXXX"
|
|
2774
|
+
3. Call GetSlackUser(salesforceUserId="005XXXXXXXXXXXX")
|
|
2775
|
+
4. Result: slackUserId="U12345ABCDE"
|
|
2776
|
+
5. Call SendMessageAsAgent(userId="U12345ABCDE", message="Hello team!")
|
|
2777
|
+
6. Report: "Message sent to John Doe."
|
|
2778
|
+
|
|
2779
|
+
## Example 2: Salesforce User ID Provided (Workflow B)
|
|
2780
|
+
Request: "Send message 'Deal update' to 005XXXXXXXXXXXX"
|
|
2781
|
+
Execution:
|
|
2782
|
+
1. Call GetSlackUser(salesforceUserId="005XXXXXXXXXXXX")
|
|
2783
|
+
2. Result: slackUserId="U12345ABCDE"
|
|
2784
|
+
3. Call SendMessageAsAgent(userId="U12345ABCDE", message="Deal update")
|
|
2785
|
+
4. Report: "Message sent."
|
|
2786
|
+
|
|
2787
|
+
## Example 3: Slack User ID Provided (Workflow C)
|
|
2788
|
+
Request: "Send message 'Quick sync?' to U12345ABCDE"
|
|
2789
|
+
Execution:
|
|
2790
|
+
1. Call SendMessageAsAgent(userId="U12345ABCDE", message="Quick sync?")
|
|
2791
|
+
2. Report: "Message sent."
|
|
2792
|
+
|
|
2793
|
+
## Example 4: User Not Found (Error Handling)
|
|
2794
|
+
Request: "Send message 'Hello' to Jane Smith"
|
|
2795
|
+
Execution:
|
|
2796
|
+
1. Call Identify_Record_by_Name(recordName="Jane Smith", objectApiName="User")
|
|
2797
|
+
2. Result: Empty searchResults (no user found)
|
|
2798
|
+
3. Report: "I couldn't find a Salesforce user named Jane Smith. Please check the name and try again."
|
|
2799
|
+
4. STOP - do not proceed to GetSlackUser or SendMessageAsAgent
|
|
2800
|
+
|
|
2801
|
+
## Example 5: Slack User Not Found (Error Handling)
|
|
2802
|
+
Request: "Send message 'Update' to John Doe"
|
|
2803
|
+
Execution:
|
|
2804
|
+
1. Call Identify_Record_by_Name(recordName="John Doe", objectApiName="User")
|
|
2805
|
+
2. Result: Salesforce User ID "005XXXXXXXXXXXX"
|
|
2806
|
+
3. Call GetSlackUser(salesforceUserId="005XXXXXXXXXXXX")
|
|
2807
|
+
4. Result: Error or null (Slack user not found)
|
|
2808
|
+
5. Report: "I found John Doe in Salesforce, but they don't have a connected Slack account. Would you like me to draft an email instead?"
|
|
2809
|
+
6. STOP - do not proceed to SendMessageAsAgent
|
|
2810
|
+
|
|
2811
|
+
# Strict Limitations
|
|
2812
|
+
|
|
2813
|
+
This utility ONLY handles:
|
|
2814
|
+
- Resolving Salesforce user names to User IDs (via Identify_Record_by_Name)
|
|
2815
|
+
- Getting Slack User ID from Salesforce User ID
|
|
2816
|
+
- Sending messages to Slack users
|
|
2817
|
+
|
|
2818
|
+
This utility does NOT:
|
|
2819
|
+
- Search for channels (caller provides channelId)
|
|
2820
|
+
- Draft messages (pass message content as provided)
|
|
2821
|
+
- Handle record-related operations beyond User lookup
|
|
2822
|
+
- Retry failed actions - if an action fails, report error and exit
|
|
2823
|
+
- Loop or re-attempt workflows - each workflow executes ONCE
|
|
2824
|
+
|
|
2825
|
+
CRITICAL FAILURE BEHAVIOR:
|
|
2826
|
+
- When ANY action fails (user not found, Slack user not found, send fails), immediately:
|
|
2827
|
+
1. Output error message to user
|
|
2828
|
+
2. EXIT the utility
|
|
2829
|
+
3. DO NOT retry the action
|
|
2830
|
+
4. DO NOT call any subsequent actions
|
|
2831
|
+
5. DO NOT re-enter reasoning loop
|
|
2832
|
+
- The caller (Research Specialist) receives control back with your error message
|
|
2833
|
+
- One workflow execution = One attempt only
|
|
2834
|
+
|
|
2835
|
+
If the request is out of scope, return an error immediately.
|
|
2836
|
+
actions:
|
|
2837
|
+
IdentifyRecordByName: @actions.Identify_Record_by_Name
|
|
2838
|
+
with recordName=...
|
|
2839
|
+
with objectApiName=...
|
|
2840
|
+
GetSlackUser: @actions.GetSlackUser
|
|
2841
|
+
with salesforceUserId=...
|
|
2842
|
+
SendMessageAsAgent: @actions.SendMessageAsAgent
|
|
2843
|
+
with userId=...
|
|
2844
|
+
with message=...
|
|
2845
|
+
with aiGenActionItemIdInContextInput=None
|
|
2846
|
+
Return_to_Hub: @utils.transition to @subagent.Sales_Management_Router
|
|
2847
|
+
description: "Return to the Router when the request is out of scope for Notification Utility."
|
|
2848
|
+
actions:
|
|
2849
|
+
Identify_Record_by_Name:
|
|
2850
|
+
description: "Searches for Salesforce CRM records by name and returns a list of matching Salesforce CRM record IDs. Use to resolve user names to Salesforce User IDs."
|
|
2851
|
+
label: "Identify Record by Name"
|
|
2852
|
+
require_user_confirmation: False
|
|
2853
|
+
include_in_progress_indicator: True
|
|
2854
|
+
source: "EmployeeCopilot__IdentifyRecordByName"
|
|
2855
|
+
target: "standardInvocableAction://identifyRecordByName"
|
|
2856
|
+
inputs:
|
|
2857
|
+
"recordName": string
|
|
2858
|
+
description: "The name of the Salesforce user to find (e.g., 'John Doe'). Used to find a Salesforce User record."
|
|
2859
|
+
label: "recordName"
|
|
2860
|
+
is_required: True
|
|
2861
|
+
is_user_input: True
|
|
2862
|
+
"objectApiName": string
|
|
2863
|
+
description: "The API name of the Salesforce object. For user lookups, use 'User'."
|
|
2864
|
+
label: "objectApiName"
|
|
2865
|
+
is_required: False
|
|
2866
|
+
is_user_input: False
|
|
2867
|
+
outputs:
|
|
2868
|
+
"searchResults": list[object]
|
|
2869
|
+
description: "A list of matching Salesforce User IDs in descending order of relevance."
|
|
2870
|
+
label: "searchResults"
|
|
2871
|
+
is_displayable: True
|
|
2872
|
+
filter_from_agent: False
|
|
2873
|
+
complex_data_type_name: "lightning__recordInfoType"
|
|
2874
|
+
GetSlackUser:
|
|
2875
|
+
description: "Retrieves the Slack user ID for a given Salesforce user ID. Use this to find the corresponding Slack account for a Salesforce user before sending them a direct message."
|
|
2876
|
+
label: "Get Slack User"
|
|
2877
|
+
require_user_confirmation: False
|
|
2878
|
+
include_in_progress_indicator: True
|
|
2879
|
+
progress_indicator_message: "Finding Slack user"
|
|
2880
|
+
source: "Slack__GetSlackUser"
|
|
2881
|
+
target: "standardInvocableAction://slackAgentGetUser"
|
|
2882
|
+
inputs:
|
|
2883
|
+
"salesforceUserId": object
|
|
2884
|
+
description: "The Salesforce user ID (format: 005...) to look up the corresponding Slack user."
|
|
2885
|
+
label: "Salesforce User ID"
|
|
2886
|
+
is_required: True
|
|
2887
|
+
is_user_input: False
|
|
2888
|
+
complex_data_type_name: "lightning__recordIdType"
|
|
2889
|
+
outputs:
|
|
2890
|
+
"slackUserId": string
|
|
2891
|
+
description: "The Slack user ID (format: U...) corresponding to the Salesforce user. Use this as the channelId when sending direct messages."
|
|
2892
|
+
label: "Slack User ID"
|
|
2893
|
+
is_displayable: False
|
|
2894
|
+
filter_from_agent: False
|
|
2895
|
+
SendMessageAsAgent:
|
|
2896
|
+
description: "Sends a message to a Slack user as the agent. Supports markdown formatting. Returns message URL and timestamp after sending."
|
|
2897
|
+
label: "Send Message As Agent"
|
|
2898
|
+
require_user_confirmation: False
|
|
2899
|
+
include_in_progress_indicator: True
|
|
2900
|
+
progress_indicator_message: "Sending Slack message"
|
|
2901
|
+
source: "Slack__SendMessageAsAgent"
|
|
2902
|
+
target: "slack://slackAgentDynamic__SendMessageAsAgent"
|
|
2903
|
+
inputs:
|
|
2904
|
+
"message": string
|
|
2905
|
+
description: "The message content to send. Supports standard markdown formatting (**bold**, _italic_, `code`, lists, links, code blocks). Limited to 5000 characters per text element."
|
|
2906
|
+
label: "Message"
|
|
2907
|
+
is_required: True
|
|
2908
|
+
is_user_input: True
|
|
2909
|
+
"userId": string
|
|
2910
|
+
description: "The Slack user ID to send the direct message to. Get this from Get Slack User action using a Salesforce User ID."
|
|
2911
|
+
label: "User ID"
|
|
2912
|
+
is_required: True
|
|
2913
|
+
is_user_input: False
|
|
2914
|
+
"aiGenActionItemIdInContextInput": string
|
|
2915
|
+
description: "Context variable for AI-generated action item tracking."
|
|
2916
|
+
label: "AI Gen Action Item ID"
|
|
2917
|
+
is_required: False
|
|
2918
|
+
is_user_input: False
|
|
2919
|
+
"recommendedActions": list[object]
|
|
2920
|
+
description: "Optional recommended actions to include with the message."
|
|
2921
|
+
label: "Recommended Actions"
|
|
2922
|
+
is_required: False
|
|
2923
|
+
is_user_input: False
|
|
2924
|
+
complex_data_type_name: "slack_invocable_actions__promptActionButton"
|
|
2925
|
+
outputs:
|
|
2926
|
+
"messageUrl": string
|
|
2927
|
+
description: "A URL to the sent message in Slack that can be shared with users."
|
|
2928
|
+
label: "Message URL"
|
|
2929
|
+
is_displayable: False
|
|
2930
|
+
filter_from_agent: False
|
|
2931
|
+
"messageTs": string
|
|
2932
|
+
description: "The timestamp of the sent message."
|
|
2933
|
+
label: "Message Timestamp"
|
|
2934
|
+
is_displayable: False
|
|
2935
|
+
filter_from_agent: False
|
|
2936
|
+
|
|
2937
|
+
connection slack:
|
|
2938
|
+
empty
|