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