scc-universal 1.2.1 → 1.3.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/.claude-plugin/plugin.json +1 -1
- package/.cursor/agents/sf-agentforce-agent.md +88 -40
- package/.cursor/skills/prompt-optimizer/SKILL.md +21 -21
- package/.cursor/skills/sf-2gp-security-review/SKILL.md +167 -0
- package/.cursor/skills/sf-agentforce-development/SKILL.md +385 -348
- package/.cursor/skills/sf-cli-reference/SKILL.md +221 -0
- package/.cursor/skills/sf-harness-audit/SKILL.md +2 -2
- package/.cursor/skills/sf-quickstart/SKILL.md +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +8 -38
- package/agents/sf-agentforce-agent.md +88 -40
- package/docs/ARCHITECTURE.md +4 -3
- package/docs/authoring-guide.md +1 -1
- package/docs/hook-development.md +1 -1
- package/examples/agentforce-action/README.md +4 -4
- package/examples/devops-pipeline/README.md +4 -4
- package/examples/integration-pattern/README.md +4 -4
- package/examples/platform-events/README.md +4 -4
- package/examples/security-audit/README.md +3 -3
- package/examples/visualforce-migration/README.md +4 -4
- package/manifests/install-modules.json +9 -3
- package/package.json +2 -2
- package/scripts/lib/install-executor.js +23 -12
- package/skills/_reference/AGENTFORCE_PATTERNS.md +433 -51
- package/skills/_reference/APPEXCHANGE_REVIEW.md +427 -0
- package/skills/_reference/SF_CLI_COMMANDS.md +812 -0
- package/skills/prompt-optimizer/SKILL.md +21 -21
- package/skills/sf-2gp-security-review/SKILL.md +168 -0
- package/skills/sf-agentforce-development/SKILL.md +385 -348
- package/skills/sf-cli-reference/SKILL.md +225 -0
- package/skills/sf-harness-audit/SKILL.md +2 -2
- package/skills/sf-quickstart/SKILL.md +1 -1
- package/.cursor/hooks/adapter.js +0 -81
- package/.cursor/hooks/after-file-edit.js +0 -26
- package/.cursor/hooks/after-mcp-execution.js +0 -12
- package/.cursor/hooks/after-shell-execution.js +0 -30
- package/.cursor/hooks/after-tab-file-edit.js +0 -12
- package/.cursor/hooks/before-mcp-execution.js +0 -11
- package/.cursor/hooks/before-read-file.js +0 -13
- package/.cursor/hooks/before-shell-execution.js +0 -29
- package/.cursor/hooks/before-submit-prompt.js +0 -23
- package/.cursor/hooks/pre-compact.js +0 -7
- package/.cursor/hooks/session-end.js +0 -10
- package/.cursor/hooks/session-start.js +0 -10
- package/.cursor/hooks/stop.js +0 -18
- package/.cursor/hooks/subagent-start.js +0 -10
- package/.cursor/hooks/subagent-stop.js +0 -10
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sf-agentforce-development
|
|
3
|
-
description: "Agentforce
|
|
3
|
+
description: "Agentforce agent development — Agent Script, topics, actions, testing, metadata. Use when building Agentforce agents. Do NOT use for non-Agentforce Apex or Flow-only automation."
|
|
4
4
|
origin: SCC
|
|
5
5
|
user-invocable: false
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Agentforce Development
|
|
9
9
|
|
|
10
|
-
Procedures for building Agentforce AI agents
|
|
10
|
+
Procedures for building Agentforce AI agents. Architecture, syntax reference, metadata types, instruction guidelines, and context engineering principles live in the reference file.
|
|
11
11
|
|
|
12
12
|
@../_reference/AGENTFORCE_PATTERNS.md
|
|
13
13
|
|
|
14
14
|
## When to Use
|
|
15
15
|
|
|
16
|
-
- Building Agentforce AI agents
|
|
16
|
+
- Building Agentforce AI agents with Agent Script (`.agent` files)
|
|
17
|
+
- Generating and publishing authoring bundles via CLI
|
|
17
18
|
- Configuring agent topics, actions, or conversation instructions
|
|
18
19
|
- Creating custom Apex actions or Flow actions for Agentforce
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- Reviewing Agentforce limits or security requirements
|
|
20
|
+
- Testing agents with YAML test specs, CLI, or Testing Center
|
|
21
|
+
- Deploying agent metadata (GenAi types) to orgs
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -32,419 +32,456 @@ Procedures for building Agentforce AI agents on the Salesforce platform. Archite
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## Development Approaches
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
| | Agent Script (Recommended) | Classic Setup |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| **API version** | v65+ | v60+ |
|
|
40
|
+
| **Surface** | `.agent` files in VS Code + CLI | Agentforce Builder UI |
|
|
41
|
+
| **Source control** | Yes — diffable `.agent` files | No — UI-only |
|
|
42
|
+
| **CI/CD** | Full: validate → publish → test → activate | Limited: deploy GenAi metadata |
|
|
43
|
+
| **When to use** | All new agents; teams needing code review | Orgs on API < v65; admin-managed simple agents |
|
|
38
44
|
|
|
45
|
+
**Default to Agent Script** for all new development.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Agent Script Development
|
|
50
|
+
|
|
51
|
+
### File Structure
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
force-app/main/default/aiAuthoringBundles/My_Agent/
|
|
55
|
+
My_Agent.agent # Agent Script file
|
|
56
|
+
My_Agent.bundle-meta.xml # Metadata XML
|
|
39
57
|
```
|
|
40
|
-
Topic Name: Case Management
|
|
41
|
-
Description: Handles customer service case creation, updating, and
|
|
42
|
-
status inquiries. Covers support tickets and complaints.
|
|
43
58
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
### CLI Workflow
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Generate authoring bundle (from spec or --no-spec for boilerplate)
|
|
63
|
+
sf agent generate authoring-bundle --spec specs/agentSpec.yaml \
|
|
64
|
+
--name "My Agent" --api-name My_Agent
|
|
65
|
+
|
|
66
|
+
# Edit .agent file in VS Code (syntax highlighting + validation)
|
|
67
|
+
# Validate → Publish → Preview → Activate
|
|
68
|
+
sf agent validate authoring-bundle --target-org MySandbox
|
|
69
|
+
sf agent publish authoring-bundle --target-org MySandbox
|
|
70
|
+
sf agent preview --target-org MySandbox
|
|
71
|
+
sf agent activate --api-name My_Agent --target-org MySandbox
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Complete Example
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
config:
|
|
78
|
+
developer_name: "Service_Agent"
|
|
79
|
+
agent_label: "Customer Service Agent"
|
|
80
|
+
|
|
81
|
+
variables:
|
|
82
|
+
customer_email: mutable string = ""
|
|
83
|
+
is_verified: mutable boolean = False
|
|
84
|
+
session_lang: linked string
|
|
85
|
+
source: "EndUserLanguage"
|
|
86
|
+
|
|
87
|
+
system:
|
|
88
|
+
instructions: |
|
|
89
|
+
You are a customer service agent. Be friendly and concise.
|
|
90
|
+
Always verify identity before accessing account data.
|
|
91
|
+
messages:
|
|
92
|
+
welcome: "Hello! How can I help you today?"
|
|
93
|
+
error: "I apologize. Let me transfer you to a team member."
|
|
94
|
+
|
|
95
|
+
start_agent topic_selector:
|
|
96
|
+
description: "Routes messages to the appropriate topic"
|
|
97
|
+
reasoning:
|
|
98
|
+
instructions: ->
|
|
99
|
+
| Analyze the message and determine the best topic.
|
|
100
|
+
actions:
|
|
101
|
+
verify: @utils.transition to @topic.identity
|
|
102
|
+
description: "Verify customer identity"
|
|
103
|
+
orders: @utils.transition to @topic.order_management
|
|
104
|
+
description: "Help with order status or modifications"
|
|
105
|
+
|
|
106
|
+
topic identity:
|
|
107
|
+
description: "Verifies customer identity via email lookup"
|
|
108
|
+
reasoning:
|
|
109
|
+
instructions: ->
|
|
110
|
+
if @variables.is_verified == True:
|
|
111
|
+
| Customer is already verified.
|
|
112
|
+
transition to @topic.order_management
|
|
113
|
+
| Ask for their email address to verify identity.
|
|
114
|
+
actions:
|
|
115
|
+
lookup:
|
|
116
|
+
action: @actions.lookup_customer
|
|
117
|
+
description: "Look up customer by email"
|
|
118
|
+
inputs:
|
|
119
|
+
email: ...
|
|
120
|
+
set:
|
|
121
|
+
is_verified: output.found
|
|
122
|
+
|
|
123
|
+
after_reasoning: ->
|
|
124
|
+
if @variables.is_verified == True:
|
|
125
|
+
transition to @topic.order_management
|
|
126
|
+
|
|
127
|
+
topic order_management:
|
|
128
|
+
description: "Helps customers check order status"
|
|
129
|
+
reasoning:
|
|
130
|
+
instructions: ->
|
|
131
|
+
if @variables.is_verified == False:
|
|
132
|
+
transition to @topic.identity
|
|
133
|
+
| Help the customer with their order inquiry.
|
|
134
|
+
actions:
|
|
135
|
+
get_order:
|
|
136
|
+
action: @actions.get_order_details
|
|
137
|
+
description: "Retrieve order details by order number"
|
|
138
|
+
inputs:
|
|
139
|
+
order_number: ...
|
|
140
|
+
customer_email: @variables.customer_email
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Key Patterns
|
|
144
|
+
|
|
145
|
+
**Deterministic** (`->`) — guaranteed execution:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
-> if @variables.is_verified == False:
|
|
149
|
+
transition to @topic.identity
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**LLM prompts** (`|`) — accumulated and sent to model:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
| Help the customer with their order.
|
|
156
|
+
| Be concise and provide the order number.
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Deterministic action** (`run`) — bypasses LLM:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
-> run @actions.get_recent_orders with
|
|
163
|
+
customer_id: @variables.customer_id
|
|
164
|
+
set
|
|
165
|
+
recent_orders: result
|
|
54
166
|
```
|
|
55
167
|
|
|
56
168
|
---
|
|
57
169
|
|
|
58
|
-
##
|
|
170
|
+
## Action Types
|
|
59
171
|
|
|
60
|
-
###
|
|
172
|
+
### Apex @InvocableMethod
|
|
173
|
+
|
|
174
|
+
Apex actions are built by `sf-apex-agent` using patterns from `sf-apex-constraints`. Key Agentforce-specific requirements for `@InvocableMethod` actions:
|
|
175
|
+
|
|
176
|
+
- **Labels/descriptions are critical** — the LLM reads these to decide routing. Keep in sync between Apex annotations and Builder action config
|
|
177
|
+
- **InvocableVariable descriptions** must specify data type and format: `"accountId — 18-digit Account record ID"`
|
|
178
|
+
- **Return result objects** (not void) — agent needs structured confirmation to continue reasoning
|
|
179
|
+
- **Use `Database` class** (partial success) not DML verbs (all-or-nothing)
|
|
180
|
+
- **Varied verb names** across actions: "Locate", "Retrieve", "Calculate" — not "Get X", "Get Y"
|
|
181
|
+
- **Decompose** complex actions to avoid CPU timeout (10s sync limit)
|
|
182
|
+
- **Long-running work**: enqueue Queueable, return requestId for status tracking
|
|
183
|
+
|
|
184
|
+
### MCP Server
|
|
61
185
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Description = req.description,
|
|
82
|
-
Priority = req.priority != null ? req.priority : 'Medium',
|
|
83
|
-
Status = 'New',
|
|
84
|
-
Origin = 'Agentforce'
|
|
85
|
-
));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
List<Database.SaveResult> saveResults =
|
|
89
|
-
Database.insert(casesToInsert, false, AccessLevel.USER_MODE);
|
|
90
|
-
|
|
91
|
-
Set<Id> successIds = new Set<Id>();
|
|
92
|
-
for (Database.SaveResult sr : saveResults) {
|
|
93
|
-
if (sr.isSuccess()) successIds.add(sr.getId());
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
Map<Id, Case> caseMap = new Map<Id, Case>(
|
|
97
|
-
[SELECT Id, CaseNumber FROM Case WHERE Id IN :successIds]
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
for (Database.SaveResult sr : saveResults) {
|
|
101
|
-
CreateCaseResult result = new CreateCaseResult();
|
|
102
|
-
if (sr.isSuccess()) {
|
|
103
|
-
result.caseId = sr.getId();
|
|
104
|
-
result.caseNumber = caseMap.get(sr.getId())?.CaseNumber;
|
|
105
|
-
result.success = true;
|
|
106
|
-
result.message = 'Case created: ' + result.caseNumber;
|
|
107
|
-
} else {
|
|
108
|
-
result.success = false;
|
|
109
|
-
result.message = 'Failed: ' + sr.getErrors()[0].getMessage();
|
|
110
|
-
}
|
|
111
|
-
results.add(result);
|
|
112
|
-
}
|
|
113
|
-
return results;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
public class CreateCaseRequest {
|
|
117
|
-
@InvocableVariable(label='Account ID'
|
|
118
|
-
description='Salesforce Account ID of the customer'
|
|
119
|
-
required=true)
|
|
120
|
-
public Id accountId;
|
|
121
|
-
|
|
122
|
-
@InvocableVariable(label='Contact ID'
|
|
123
|
-
description='Contact ID raising the case' required=false)
|
|
124
|
-
public Id contactId;
|
|
125
|
-
|
|
126
|
-
@InvocableVariable(label='Subject'
|
|
127
|
-
description='Brief issue description (max 255 chars)'
|
|
128
|
-
required=true)
|
|
129
|
-
public String subject;
|
|
130
|
-
|
|
131
|
-
@InvocableVariable(label='Description'
|
|
132
|
-
description='Detailed issue description' required=false)
|
|
133
|
-
public String description;
|
|
134
|
-
|
|
135
|
-
@InvocableVariable(label='Priority'
|
|
136
|
-
description='Low, Medium, High, Critical' required=false)
|
|
137
|
-
public String priority;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
public class CreateCaseResult {
|
|
141
|
-
@InvocableVariable(label='Case ID')
|
|
142
|
-
public Id caseId;
|
|
143
|
-
|
|
144
|
-
@InvocableVariable(label='Case Number')
|
|
145
|
-
public String caseNumber;
|
|
146
|
-
|
|
147
|
-
@InvocableVariable(label='Success')
|
|
148
|
-
public Boolean success;
|
|
149
|
-
|
|
150
|
-
@InvocableVariable(label='Message')
|
|
151
|
-
public String message;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
186
|
+
External tools exposed to Agentforce via Model Context Protocol (JSON-RPC 2.0 over HTTP/SSE).
|
|
187
|
+
|
|
188
|
+
- **Setup**: Register server in MCP Server Registry (Setup > MCP Servers). Tools appear in Agentforce Asset Library
|
|
189
|
+
- **Auth**: OAuth 2.0 with Integration User. Principle of least privilege. FLS and sharing rules enforced
|
|
190
|
+
- **Rate limit**: ~50 requests/min/server
|
|
191
|
+
- **Tool discovery**: On connection, server returns schema with tool names, descriptions, required/optional parameters, return types
|
|
192
|
+
- **Prebuilt servers**: Salesforce DX MCP Server (deploy, test, scratch orgs), Heroku Platform, MuleSoft
|
|
193
|
+
- **Hosted MCP (Pilot)**: Fully managed cloud endpoints — zero infrastructure. Pre-built for core CRM and B2C Commerce APIs
|
|
194
|
+
|
|
195
|
+
### Named Query (GA)
|
|
196
|
+
|
|
197
|
+
Parameterized SOQL exposed as REST API endpoint and agent action. No Apex or Flow required.
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
Query Name: GetRecentOrders
|
|
201
|
+
SOQL: SELECT Id, Name, Status__c, CreatedDate
|
|
202
|
+
FROM Order__c
|
|
203
|
+
WHERE AccountId = :accountId
|
|
204
|
+
ORDER BY CreatedDate DESC LIMIT 5
|
|
154
205
|
```
|
|
155
206
|
|
|
156
|
-
|
|
207
|
+
- Auto-creates REST endpoint: `/services/data/v66.0/named/query/GetRecentOrders?accountId=001xx...`
|
|
208
|
+
- Activate in API Catalog, then add as agent action
|
|
209
|
+
- **Limitation**: Input parameters only in WHERE and LIMIT clauses. Cannot edit after agent action activation without deactivation
|
|
210
|
+
|
|
211
|
+
### AuraEnabled Methods (Beta)
|
|
212
|
+
|
|
213
|
+
Reuse existing `@AuraEnabled` controller methods as agent actions:
|
|
214
|
+
|
|
215
|
+
1. Generate OpenAPI spec: VS Code → "SFDX: Create OpenAPI Document from This Class" → creates `.yaml` + `.externalServiceRegistration-meta.xml`
|
|
216
|
+
1. Optionally add `x-sfdc: publishAsAgentAction: true` for auto-creation
|
|
217
|
+
1. Deploy to API Catalog
|
|
218
|
+
1. Add as agent action (category: "AuraEnabled Method (Beta)")
|
|
219
|
+
|
|
220
|
+
### Apex Citations
|
|
221
|
+
|
|
222
|
+
Source attribution in agent responses. Two approaches:
|
|
157
223
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
224
|
+
| Class | Behavior | Use When |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| `AiCopilot.GenAiCitationInput` | Supplies sources to reasoning engine; engine auto-places inline numbered citations | Default — let the engine decide placement |
|
|
227
|
+
| `AiCopilot.GenAiCitationOutput` | Direct control over citation placement; bypasses reasoning engine | Predetermined citation logic needed |
|
|
228
|
+
|
|
229
|
+
Supported sources: knowledge articles, PDF files, external web pages. Requires agent created after May 2025. Action must return both generated response and citation metadata.
|
|
230
|
+
|
|
231
|
+
### Lightning Types
|
|
232
|
+
|
|
233
|
+
Custom LWC components for rich conversational UI in agent responses.
|
|
166
234
|
|
|
167
|
-
// Return a result object (agent needs confirmation), not void
|
|
168
|
-
// Bulkify — agent runtime may batch calls
|
|
169
235
|
```
|
|
236
|
+
lightningTypes/
|
|
237
|
+
flightResponse/
|
|
238
|
+
schema.json # JSON Schema for validation
|
|
239
|
+
lightningDesktopGenAi/ # Channel: Employee Agent
|
|
240
|
+
renderer.json # Output LWC component
|
|
241
|
+
enhancedWebChat/ # Channel: Service Agent (Enhanced Chat v2)
|
|
242
|
+
renderer.json
|
|
243
|
+
flightFilter/
|
|
244
|
+
schema.json
|
|
245
|
+
lightningDesktopGenAi/
|
|
246
|
+
editor.json # Input LWC component
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- LWC targets: `lightning__AgentforceInput` (editor), `lightning__AgentforceOutput` (renderer)
|
|
250
|
+
- **Constraint**: Custom Lightning Types only override UI for actions using **Apex classes** as input/output
|
|
251
|
+
- Editor LWC must include `handleInputChange()` dispatching `valuechange` event
|
|
252
|
+
|
|
253
|
+
### Adaptive Response Formats
|
|
254
|
+
|
|
255
|
+
Rich responses without custom LWC development (Service Agents on messaging channels):
|
|
256
|
+
|
|
257
|
+
| Format | UI | Fields |
|
|
258
|
+
|---|---|---|
|
|
259
|
+
| **Rich Choice** | Carousel, buttons, list selector | Title, description, link, image per tile |
|
|
260
|
+
| **Rich Link** | Media card | Link title, URL, image, description |
|
|
261
|
+
|
|
262
|
+
Determined automatically by returned data structure from Apex or Flow actions.
|
|
170
263
|
|
|
171
264
|
---
|
|
172
265
|
|
|
173
|
-
## Flow Actions
|
|
266
|
+
## Flow Actions
|
|
174
267
|
|
|
175
|
-
Use
|
|
268
|
+
Use when logic involves declarative orchestration or non-developer maintenance.
|
|
176
269
|
|
|
177
270
|
```
|
|
178
|
-
Flow: Update_Account_Segment (Autolaunched
|
|
179
|
-
Variables:
|
|
180
|
-
|
|
181
|
-
- newSegment (Input, Text, Required)
|
|
182
|
-
- result (Output, Text)
|
|
183
|
-
|
|
184
|
-
Steps:
|
|
185
|
-
1. Get Records: Account WHERE Id = {accountId}
|
|
186
|
-
2. Decision: Is segment different from current?
|
|
187
|
-
3. Update Records: Account.Segment__c = {newSegment}
|
|
188
|
-
4. Assignment: result = "Segment updated to " + {newSegment}
|
|
271
|
+
Flow: Update_Account_Segment (Autolaunched)
|
|
272
|
+
Variables: accountId (Input), newSegment (Input), result (Output)
|
|
273
|
+
Steps: Get Record → Decision → Update Record → Assign result
|
|
189
274
|
```
|
|
190
275
|
|
|
191
|
-
Add
|
|
276
|
+
Add to topics in Setup or reference in Agent Script `reasoning.actions`.
|
|
192
277
|
|
|
193
278
|
---
|
|
194
279
|
|
|
195
280
|
## Prompt Templates
|
|
196
281
|
|
|
197
|
-
### Creating a Flex Prompt Template
|
|
198
|
-
|
|
199
282
|
```
|
|
200
283
|
Template Name: Case Summary for Agent
|
|
201
|
-
Template Type: Flex
|
|
284
|
+
Template Type: Flex
|
|
202
285
|
Grounding: Case record
|
|
203
286
|
|
|
204
|
-
|
|
205
|
-
You are a helpful customer service assistant.
|
|
287
|
+
Body:
|
|
206
288
|
Summarise the following case for a support representative.
|
|
207
|
-
|
|
208
|
-
Case Details:
|
|
209
289
|
- Case Number: {!$Input:Case.CaseNumber}
|
|
210
290
|
- Subject: {!$Input:Case.Subject}
|
|
211
291
|
- Status: {!$Input:Case.Status}
|
|
212
|
-
- Priority: {!$Input:Case.Priority}
|
|
213
292
|
- Customer: {!$Input:Case.Account.Name}
|
|
214
|
-
- Description: {!$Input:Case.Description}
|
|
215
293
|
|
|
216
|
-
Provide:
|
|
217
|
-
1. A 2-sentence summary of the issue
|
|
218
|
-
2. Recommended next action
|
|
219
|
-
3. Estimated complexity: Low/Medium/High
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Using Prompt Templates in Apex
|
|
223
|
-
|
|
224
|
-
> Note: The ConnectApi surface for Einstein/Agentforce changes rapidly. Verify exact class/method names against the ConnectApi Apex Reference for your target API version (v66.0 Spring '26).
|
|
225
|
-
|
|
226
|
-
```apex
|
|
227
|
-
public with sharing class PromptTemplateAction {
|
|
228
|
-
|
|
229
|
-
@InvocableMethod(
|
|
230
|
-
label='Generate Case Summary'
|
|
231
|
-
description='Generates an AI summary using Einstein Prompt Template'
|
|
232
|
-
)
|
|
233
|
-
public static List<SummaryResult> generateCaseSummary(
|
|
234
|
-
List<SummaryRequest> requests) {
|
|
235
|
-
List<SummaryResult> results = new List<SummaryResult>();
|
|
236
|
-
|
|
237
|
-
for (SummaryRequest req : requests) {
|
|
238
|
-
try {
|
|
239
|
-
ConnectApi.EinsteinPromptTemplateGenerationsRepresentation
|
|
240
|
-
response = ConnectApi.EinsteinLLM
|
|
241
|
-
.generateMessagesForPromptTemplate(
|
|
242
|
-
'Case_Summary_for_Agent',
|
|
243
|
-
new Map<String, String>{
|
|
244
|
-
'Input:Case' => req.caseId
|
|
245
|
-
},
|
|
246
|
-
new Map<String, ConnectApi
|
|
247
|
-
.EinsteinPromptTemplateGenerationsInput>()
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
SummaryResult result = new SummaryResult();
|
|
251
|
-
result.summary = response.generations[0].text;
|
|
252
|
-
result.success = true;
|
|
253
|
-
results.add(result);
|
|
254
|
-
} catch (Exception e) {
|
|
255
|
-
SummaryResult result = new SummaryResult();
|
|
256
|
-
result.success = false;
|
|
257
|
-
result.errorMessage = e.getMessage();
|
|
258
|
-
results.add(result);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return results;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
public class SummaryRequest {
|
|
265
|
-
@InvocableVariable(label='Case ID' required=true)
|
|
266
|
-
public Id caseId;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
public class SummaryResult {
|
|
270
|
-
@InvocableVariable(label='Summary')
|
|
271
|
-
public String summary;
|
|
272
|
-
@InvocableVariable(label='Success')
|
|
273
|
-
public Boolean success;
|
|
274
|
-
@InvocableVariable(label='Error Message')
|
|
275
|
-
public String errorMessage;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
294
|
+
Provide: 1) 2-sentence summary 2) Recommended next action 3) Complexity: Low/Medium/High
|
|
278
295
|
```
|
|
279
296
|
|
|
280
297
|
---
|
|
281
298
|
|
|
282
|
-
##
|
|
299
|
+
## Testing
|
|
300
|
+
|
|
301
|
+
Apex unit tests for `@InvocableMethod` actions are handled by `sf-apex-agent` using `sf-testing-constraints`. This section covers **agent-level testing** — verifying topic routing, action execution, and response quality.
|
|
302
|
+
|
|
303
|
+
### Agent Test Spec (YAML)
|
|
304
|
+
|
|
305
|
+
```yaml
|
|
306
|
+
name: Service_Agent_Tests
|
|
307
|
+
description: End-to-end tests for Customer Service Agent
|
|
308
|
+
subjectType: AGENT
|
|
309
|
+
subjectName: Service_Agent
|
|
310
|
+
subjectVersion: v1
|
|
311
|
+
testCases:
|
|
312
|
+
- utterance: "What's the status of order #12345?"
|
|
313
|
+
expectedTopic: Order_Management
|
|
314
|
+
expectedActions:
|
|
315
|
+
- get_order_details
|
|
316
|
+
expectedOutcome: "Agent provides order status details including shipping info"
|
|
317
|
+
contextVariables:
|
|
318
|
+
- name: EndUserLanguage
|
|
319
|
+
value: en
|
|
320
|
+
metrics:
|
|
321
|
+
- topic_sequence_match
|
|
322
|
+
- action_sequence_match
|
|
323
|
+
- bot_response_rating
|
|
324
|
+
- coherence
|
|
325
|
+
- completeness
|
|
326
|
+
- conciseness
|
|
327
|
+
- latency
|
|
328
|
+
|
|
329
|
+
- utterance: "I need to file a complaint about my delivery"
|
|
330
|
+
expectedTopic: Case_Management
|
|
331
|
+
expectedActions:
|
|
332
|
+
- create_support_case
|
|
333
|
+
expectedOutcome: "Agent creates a case and provides the case number"
|
|
334
|
+
metrics:
|
|
335
|
+
- topic_sequence_match
|
|
336
|
+
- action_sequence_match
|
|
337
|
+
- bot_response_rating
|
|
338
|
+
- instructionAdherence
|
|
339
|
+
|
|
340
|
+
- utterance: "Hola, necesito ayuda con mi pedido"
|
|
341
|
+
expectedTopic: Order_Management
|
|
342
|
+
contextVariables:
|
|
343
|
+
- name: EndUserLanguage
|
|
344
|
+
value: es
|
|
345
|
+
metrics:
|
|
346
|
+
- topic_sequence_match
|
|
347
|
+
- coherence
|
|
348
|
+
```
|
|
283
349
|
|
|
284
|
-
|
|
350
|
+
### Multi-Turn Conversation Testing
|
|
351
|
+
|
|
352
|
+
```yaml
|
|
353
|
+
testCases:
|
|
354
|
+
- utterance: "I want to return my order"
|
|
355
|
+
expectedTopic: Order_Management
|
|
356
|
+
conversationHistory:
|
|
357
|
+
- role: user
|
|
358
|
+
message: "Hi, I need help"
|
|
359
|
+
- role: agent
|
|
360
|
+
message: "Hello! How can I help you today?"
|
|
361
|
+
topic: topic_selector
|
|
362
|
+
- role: user
|
|
363
|
+
message: "I have a problem with order #12345"
|
|
364
|
+
- role: agent
|
|
365
|
+
message: "I found order #12345. It was delivered on March 15."
|
|
366
|
+
topic: Order_Management
|
|
367
|
+
metrics:
|
|
368
|
+
- topic_sequence_match
|
|
369
|
+
- action_sequence_match
|
|
370
|
+
```
|
|
285
371
|
|
|
286
|
-
|
|
372
|
+
### Custom Evaluations
|
|
287
373
|
|
|
374
|
+
```yaml
|
|
375
|
+
testCases:
|
|
376
|
+
- utterance: "What's the weather in SF?"
|
|
377
|
+
customEvaluations:
|
|
378
|
+
- label: Temperature Check
|
|
379
|
+
jsonPathExpression: $.actions[0].result.temperature
|
|
380
|
+
comparisonOperator: greaterThan
|
|
381
|
+
expectedValue: "0"
|
|
288
382
|
```
|
|
289
|
-
Topic: CaseTriage
|
|
290
|
-
|
|
291
|
-
Step 1 (Deterministic — Apex):
|
|
292
|
-
Call: EscalateCaseAction
|
|
293
|
-
Condition: case.Priority == 'Critical'
|
|
294
383
|
|
|
295
|
-
|
|
296
|
-
Call: GetKnowledgeArticles
|
|
297
|
-
Input: case.Subject
|
|
298
|
-
-> Grounds the LLM with relevant articles
|
|
384
|
+
### CLI Test Workflow
|
|
299
385
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
386
|
+
```bash
|
|
387
|
+
# Generate test spec from agent definition
|
|
388
|
+
sf agent generate test-spec --output-file specs/testSpec.yaml
|
|
303
389
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
-> Audit trail logged regardless of LLM output
|
|
307
|
-
```
|
|
390
|
+
# Create test in org from YAML spec
|
|
391
|
+
sf agent test create --spec specs/testSpec.yaml --target-org MySandbox
|
|
308
392
|
|
|
309
|
-
|
|
393
|
+
# Run tests synchronously with JUnit output (CI-friendly)
|
|
394
|
+
sf agent test run --api-name Service_Agent_Tests --wait 10 \
|
|
395
|
+
--result-format junit --output-dir ./test-results \
|
|
396
|
+
--target-org MySandbox
|
|
310
397
|
|
|
311
|
-
|
|
398
|
+
# View results
|
|
399
|
+
sf agent test results --test-id <id> --target-org MySandbox
|
|
312
400
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
### Unit Testing Apex Actions
|
|
316
|
-
|
|
317
|
-
```apex
|
|
318
|
-
@IsTest
|
|
319
|
-
public class CaseManagementActionTest {
|
|
320
|
-
|
|
321
|
-
@TestSetup
|
|
322
|
-
static void setup() {
|
|
323
|
-
Account acc = new Account(Name='Test Account');
|
|
324
|
-
insert acc;
|
|
325
|
-
Contact con = new Contact(LastName='Doe', AccountId=acc.Id);
|
|
326
|
-
insert con;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
@IsTest
|
|
330
|
-
static void testCreateCase_validInput_createsCase() {
|
|
331
|
-
Account acc = [SELECT Id FROM Account LIMIT 1];
|
|
332
|
-
Contact con = [SELECT Id FROM Contact LIMIT 1];
|
|
333
|
-
|
|
334
|
-
CaseManagementAction.CreateCaseRequest req =
|
|
335
|
-
new CaseManagementAction.CreateCaseRequest();
|
|
336
|
-
req.accountId = acc.Id;
|
|
337
|
-
req.contactId = con.Id;
|
|
338
|
-
req.subject = 'Cannot access portal';
|
|
339
|
-
req.priority = 'High';
|
|
340
|
-
|
|
341
|
-
Test.startTest();
|
|
342
|
-
List<CaseManagementAction.CreateCaseResult> results =
|
|
343
|
-
CaseManagementAction.createSupportCase(
|
|
344
|
-
new List<CaseManagementAction.CreateCaseRequest>{req});
|
|
345
|
-
Test.stopTest();
|
|
346
|
-
|
|
347
|
-
System.assert(results[0].success);
|
|
348
|
-
System.assertNotEquals(null, results[0].caseId);
|
|
349
|
-
|
|
350
|
-
Case created = [SELECT Status, Origin, Priority
|
|
351
|
-
FROM Case WHERE Id = :results[0].caseId];
|
|
352
|
-
System.assertEquals('Agentforce', created.Origin);
|
|
353
|
-
System.assertEquals('High', created.Priority);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
@IsTest
|
|
357
|
-
static void testCreateCase_bulk_createsMultipleCases() {
|
|
358
|
-
Account acc = [SELECT Id FROM Account LIMIT 1];
|
|
359
|
-
|
|
360
|
-
List<CaseManagementAction.CreateCaseRequest> requests =
|
|
361
|
-
new List<CaseManagementAction.CreateCaseRequest>();
|
|
362
|
-
for (Integer i = 0; i < 200; i++) {
|
|
363
|
-
CaseManagementAction.CreateCaseRequest req =
|
|
364
|
-
new CaseManagementAction.CreateCaseRequest();
|
|
365
|
-
req.accountId = acc.Id;
|
|
366
|
-
req.subject = 'Bulk test case ' + i;
|
|
367
|
-
requests.add(req);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
Test.startTest();
|
|
371
|
-
List<CaseManagementAction.CreateCaseResult> results =
|
|
372
|
-
CaseManagementAction.createSupportCase(requests);
|
|
373
|
-
Test.stopTest();
|
|
374
|
-
|
|
375
|
-
Integer successCount = 0;
|
|
376
|
-
for (CaseManagementAction.CreateCaseResult r : results) {
|
|
377
|
-
if (r.success) successCount++;
|
|
378
|
-
}
|
|
379
|
-
System.assertEquals(200, successCount);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
401
|
+
# List all agent tests
|
|
402
|
+
sf agent test list --target-org MySandbox
|
|
382
403
|
```
|
|
383
404
|
|
|
384
|
-
|
|
405
|
+
### Testing REST API (CI Integration)
|
|
385
406
|
|
|
386
|
-
|
|
407
|
+
```
|
|
408
|
+
# Start test run
|
|
409
|
+
POST /services/data/v63.0/einstein/ai-evaluations/runs
|
|
410
|
+
Body: { "aiEvaluationDefinitionName": "Service_Agent_Tests" }
|
|
411
|
+
Response: { "runId": "0Xx..." }
|
|
412
|
+
|
|
413
|
+
# Poll status
|
|
414
|
+
GET /services/data/v63.0/einstein/ai-evaluations/runs/{runId}
|
|
415
|
+
Response: { "status": "NEW | IN_PROGRESS | COMPLETED | ERROR" }
|
|
416
|
+
|
|
417
|
+
# Get results
|
|
418
|
+
GET /services/data/v63.0/einstein/ai-evaluations/runs/{runId}/results
|
|
419
|
+
Response: { "testCases": [...], "testResults": [...] }
|
|
420
|
+
```
|
|
387
421
|
|
|
388
|
-
|
|
389
|
-
# Activate an agent
|
|
390
|
-
sf agent activate --name "Sales Assistant" --target-org MySandbox
|
|
422
|
+
Auth: OAuth 2.0 via External Client App (JWT with consumer key/secret).
|
|
391
423
|
|
|
392
|
-
|
|
393
|
-
sf agent test run --target-org MySandbox --output-dir test-results/
|
|
424
|
+
### Testing Tools Summary
|
|
394
425
|
|
|
395
|
-
|
|
396
|
-
|
|
426
|
+
| Tool | Purpose |
|
|
427
|
+
|---|---|
|
|
428
|
+
| **Agent Builder Preview** | Real-time conversational testing (simulated or live mode) |
|
|
429
|
+
| **Agentforce Testing Center** | Bulk test execution; auto-generates test cases from knowledge |
|
|
430
|
+
| **CLI (`sf agent test`)** | Headless testing, JUnit output, CI pipeline integration |
|
|
431
|
+
| **VS Code Agent Panel** | View/run tests + Agent Preview pane + Apex Replay Debugger |
|
|
432
|
+
| **Testing REST API** | Programmatic test execution from external CI systems |
|
|
433
|
+
| **Agent Grid (Beta)** | Spreadsheet-like rapid testing with real CRM data |
|
|
397
434
|
|
|
398
|
-
|
|
399
|
-
sf agent test results --job-id <jobId> --result-format human
|
|
435
|
+
---
|
|
400
436
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
437
|
+
## Metadata & Deployment
|
|
438
|
+
|
|
439
|
+
```
|
|
440
|
+
force-app/main/default/
|
|
441
|
+
aiAuthoringBundles/My_Agent/ # .agent + .bundle-meta.xml
|
|
442
|
+
bots/My_Agent/ # .bot-meta.xml
|
|
443
|
+
botVersions/My_Agent.v1/ # .botVersion-meta.xml
|
|
444
|
+
genAiPlannerBundles/My_Agent/ # .genAiPlannerBundle-meta.xml
|
|
445
|
+
genAiPlugins/Topic_Name/ # .genAiPlugin-meta.xml
|
|
446
|
+
genAiFunctions/Action_Name/ # .genAiFunction-meta.xml + input/output schema.json
|
|
447
|
+
aiEvaluationDefinitions/ # .aiEvaluationDefinition-meta.xml
|
|
406
448
|
```
|
|
407
449
|
|
|
408
|
-
|
|
450
|
+
**Deploy order**: Bot/BotVersion → GenAiPromptTemplate → GenAiFunction → GenAiPlugin → GenAiPlannerBundle → AiAuthoringBundle → AiEvaluationDefinition → Activate
|
|
409
451
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
// Enforce sharing in Agentforce Apex actions
|
|
414
|
-
public with sharing class SecureAgentAction {
|
|
415
|
-
|
|
416
|
-
@InvocableMethod(label='Get Customer Orders')
|
|
417
|
-
public static List<OrderResult> getOrders(List<OrderRequest> requests) {
|
|
418
|
-
// Collect ALL accountIds — runtime may batch multiple requests
|
|
419
|
-
Set<Id> accountIds = new Set<Id>();
|
|
420
|
-
for (OrderRequest req : requests) {
|
|
421
|
-
accountIds.add(req.accountId);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// USER_MODE enforces CRUD/FLS and sharing rules
|
|
425
|
-
List<Order__c> orders = [
|
|
426
|
-
SELECT Id, Name, Status__c, Amount__c, AccountId
|
|
427
|
-
FROM Order__c
|
|
428
|
-
WHERE AccountId IN :accountIds
|
|
429
|
-
WITH USER_MODE
|
|
430
|
-
LIMIT 50
|
|
431
|
-
];
|
|
432
|
-
|
|
433
|
-
// Build results grouped by AccountId...
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
```
|
|
452
|
+
**Retrieve**: `sf project retrieve start --metadata "AiAuthoringBundle:My_Agent*"`
|
|
453
|
+
|
|
454
|
+
---
|
|
437
455
|
|
|
438
|
-
|
|
456
|
+
## Security
|
|
439
457
|
|
|
440
|
-
-
|
|
458
|
+
- Always use `with sharing` and `AccessLevel.USER_MODE` / `WITH USER_MODE`
|
|
441
459
|
- Ground Prompt Templates only with fields the user's profile can read
|
|
442
460
|
- Review agent conversations in Setup > Agent Conversations
|
|
443
461
|
|
|
444
462
|
---
|
|
445
463
|
|
|
464
|
+
## Classic Topics (Pre-Agent Script)
|
|
465
|
+
|
|
466
|
+
For orgs on API < v65, configure topics in Agentforce Builder UI:
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
Topic: Case Management
|
|
470
|
+
Description: Handles case creation, updating, and status inquiries.
|
|
471
|
+
Scope WILL: Case creation, status checks, escalation
|
|
472
|
+
Scope WILL NOT: Billing disputes, resolution timeline promises
|
|
473
|
+
Instructions:
|
|
474
|
+
1. Verify customer identity before accessing case details
|
|
475
|
+
2. Create a new case if no existing open case matches
|
|
476
|
+
3. Escalate if customer is frustrated or issue is complex
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
All instruction guidelines and context engineering principles from the reference apply identically.
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
446
483
|
## Related
|
|
447
484
|
|
|
448
|
-
- Agent: `sf-agentforce-agent`
|
|
485
|
+
- Agent: `sf-agentforce-agent` — for interactive guidance
|
|
449
486
|
- Constraints: sf-apex-constraints
|
|
450
487
|
- Reference: @../_reference/AGENTFORCE_PATTERNS.md
|