2026-03-19 05:17:32 +08:00
# Agent User Setup & Permission Model
Complete provisioning workflow for Einstein Agent Users and permission sets. Validated against ORM1, ORM2, AutomotiveSupport, and SalesforceProductAssistant agents.
---
## License Requirement
PID_DigitalAgent (typically included with Agentforce licenses)
## Agent Type Decision Matrix
| Aspect | AgentforceServiceAgent | AgentforceEmployeeAgent |
|--------|------------------------|-------------------------|
| **Use Case** | Customer-facing, external users | Internal employees |
| **Runs As** | Dedicated Einstein Agent User | Logged-in user |
| **Einstein Agent User?** | Required | Not needed |
| **System PS (`AgentforceServiceAgentUser`)** | Required | Not needed |
| **Custom PS (`{AgentName}_Access`)** | Assigned to agent user | Assigned to employees |
| ** `default_agent_user` in config** | Required | Omit entirely |
| **Respects Sharing Rules** | No (consistent permissions) | Yes (user's data access) |
2026-04-09 19:34:48 +08:00
**How to check agent type**: Look at the `agent_type` field in the `config:` block of your `.agent` file, or query: `sf data query --json --query "SELECT DeveloperName, Type FROM BotDefinition WHERE DeveloperName = 'AgentName'" -o TARGET_ORG`
2026-03-19 05:17:32 +08:00
---
## CLI Fast Track: Complete Workflow
For CLI-first workflow (tested: ~8 minutes total):
```bash
# Step 1: Query existing Einstein Agent Users (30 seconds)
2026-04-09 19:34:48 +08:00
sf data query --json \
2026-03-19 05:17:32 +08:00
--query "SELECT Id, Username, IsActive FROM User WHERE Profile.Name = 'Einstein Agent User' AND IsActive = true" \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
# Step 2: Create Einstein Agent User (2 minutes)
2026-04-09 19:34:48 +08:00
# Get Profile ID (read result.records[0].Id from JSON response)
sf data query --json \
2026-03-19 05:17:32 +08:00
--query "SELECT Id FROM Profile WHERE Name = 'Einstein Agent User'" \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
# For Production/Sandbox (non-scratch org):
2026-04-09 19:34:48 +08:00
# Use the ProfileId from the query above
sf data create record --json --sobject User --values \
2026-03-19 05:17:32 +08:00
"Username=< agent_name > _user@< orgId > .ext \
LastName=< AgentName > \
Email=admin@example.com \
Alias=< alias > \
TimeZoneSidKey=America/Los_Angeles \
LocaleSidKey=en_US \
EmailEncodingKey=UTF-8 \
2026-04-09 19:34:48 +08:00
ProfileId=< PROFILE_ID > \
2026-03-19 05:17:32 +08:00
LanguageLocaleKey=en_US" \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
# For Scratch Orgs (use user definition file):
# sf org create user --definition-file config/einstein-agent-user.json -o TARGET_ORG
# Step 3: Assign System Permission Set (1 minute)
2026-04-09 19:34:48 +08:00
sf org assign permset --json \
2026-03-19 05:17:32 +08:00
--name AgentforceServiceAgentUser \
--on-behalf-of < agent_name > _user@< orgId > .ext \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
# Step 4: Deploy Custom Permission Set (3 minutes)
# (Create the .permissionset-meta.xml file first - see Section 3.2 template)
2026-04-09 19:34:48 +08:00
sf project deploy start --json \
2026-03-19 05:17:32 +08:00
--metadata PermissionSet:< AgentName > _Access \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
# Assign custom PS
2026-04-09 19:34:48 +08:00
sf org assign permset --json \
2026-03-19 05:17:32 +08:00
--name < AgentName > _Access \
--on-behalf-of < agent_name > _user@< orgId > .ext \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
# Step 5: Verify All Permissions (1 minute)
2026-04-09 19:34:48 +08:00
sf data query --json \
2026-03-19 05:17:32 +08:00
--query "SELECT PermissionSet.Name, PermissionSet.Label FROM PermissionSetAssignment WHERE Assignee.Username = '< agent_name > _user@< orgId > .ext' ORDER BY PermissionSet.Name" \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
# Expected: AgentforceServiceAgentUser + <AgentName>_Access
# Step 6: Deploy Agent Bundle (unpublished metadata)
2026-04-09 19:34:48 +08:00
sf project deploy start --json \
2026-03-19 05:17:32 +08:00
--source-dir force-app/main/default/aiAuthoringBundles/< AgentName > \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
# Step 7: Test BEFORE Publishing (recommended)
2026-04-09 19:34:48 +08:00
sf agent preview start --json \
2026-03-19 05:17:32 +08:00
--api-name < AgentName > \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
chore: rename topic to subagent for Agent Script v2 @W-21955450@ (#193)
* @W-21955450@ Rename topic to subagent for Agent Script v2
Aligns with Agent Script v2 naming standards where `topic` is renamed
to `subagent` across all skill documentation and templates.
Changes:
- Agent Script templates: topic keyword → subagent keyword
- References: @topic.* → @subagent.*
- Documentation: Updated all skill references and guides
- Natural language references preserved in comments/descriptions
* Rename start_agent topic_selector to agent_router
Completes the topic → subagent terminology alignment by:
1. Renaming start_agent from topic_selector to agent_router (15 agent files)
2. Updating template topic declarations: topic {{placeholder}} → subagent {{placeholder}} (5 files)
3. Updating all @subagent.topic_selector references to @subagent.agent_router (35 occurrences)
4. Updating documentation: prose, examples, and diagrams (10 markdown files)
5. Updating comments to use agent_router terminology
Files affected:
- 22 agent template files
- 10 documentation/reference markdown files
- Template component files
The agent_router name is more descriptive of its actual function
(routing to different subagents) and completes the Agent Script v2
terminology standardization.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Rename files with "topic" to use "subagent" terminology
Completes the topic → subagent terminology alignment by renaming
files and updating all references:
**Files renamed (5):**
- multi-topic.agent → multi-subagent.agent
- template-single-topic.agent → template-single-subagent.agent
- template-multi-topic.agent → template-multi-subagent.agent
- topic-with-actions.agent → subagent-with-actions.agent
- agent-topic-map-diagrams.md → agent-subagent-map-diagrams.md
**References updated (6 docs):**
- Updated all filename references to point to new filenames
- Updated "Topic Map" → "Subagent Map" throughout documentation
- Updated "multi-topic"/"single-topic" → "multi-subagent"/"single-subagent"
Files modified:
- README.md, SKILL.md, agent-spec-template.md
- assets/agents/README.md, assets/README-legacy.md
- references/agent-design-and-spec-creation.md
This ensures consistent "subagent" terminology across filenames,
file content, and all documentation references.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Complete topic-to-subagent terminology update across skills
Comprehensive update replacing "topic" with "subagent" terminology throughout
the developing-agentforce and testing-agentforce skills to align with Agent
Script's `subagent` block naming.
Key changes:
- "Topic Selector" → "Subagent Router" in all agent templates and docs
- "Topic/action" → "Subagent/action" in documentation
- "Topic map" → "Subagent map" in diagram references
- Updated all architecture documentation to use "subagent" terminology
- Updated 19 .agent template files with new labels and comments
- Updated 8 reference documentation files with consistent terminology
API contract preservation:
- Test spec YAML files preserve "topic" terminology to match Testing Center API
- Added clarifying comments explaining topic/subagent equivalence in YAML files
- Field names like `expectedTopic` unchanged (Salesforce API requirement)
Preserved terms:
- "off-topic" (standard phrase for out-of-scope)
- "expectedTopic" field (Testing Center API)
- "platform topics" (Salesforce guardrail features)
32 files changed, 379 insertions(+), 366 deletions(-)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Complete comprehensive topic-to-subagent terminology update
Thorough update replacing all remaining "topic" references with "subagent"
terminology across developing-agentforce, testing-agentforce, and
observing-agentforce skills to fully align with Agent Script's `subagent`
block naming.
Key changes:
- Agent Script syntax: @topic.<name> → @subagent.<name>
- Agent Script syntax: topic.actions → subagent.actions
- Shell script patterns: ^topic → ^subagent
- Documentation: "topic instructions" → "subagent instructions"
- observing-agentforce skill: Updated all agent architecture references
- Template files: Updated all inline comments and descriptions
- Variable names in scripts: TOPIC → SUBAGENT
Specific updates:
- 45 files changed, 294 insertions, 294 deletions
- Updated all Agent Script code examples to use @subagent syntax
- Updated observing-agentforce issue classification guide
- Updated shell script patterns in diagnostic tools
- Updated Apex comments to clarify topic field maps to subagents
Preserved (as required):
- "off-topic" and "off_topic" (standard out-of-scope phrase)
- Testing Center API fields: expectedTopic, topic: in YAML
- API response fields: .topic, generatedData.topic, topic_assertion
- STDM field names: ssot__TopicApiName__c (with clarifying docs)
- Template placeholders in test specs (API values)
- "Topic hash drift" (API field behavior)
- "Email topic/purpose" (means email subject)
- Explanatory comments about API field mapping
All Agent Script syntax and documentation now consistently uses "subagent"
while preserving backward compatibility with platform API field names.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* a few more topic -> subagent replacements
---------
Co-authored-by: Steve Hetzel <shetzel@salesforce.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-28 02:42:18 +08:00
# Test all subagents and actions to verify permissions
2026-03-19 05:17:32 +08:00
# Step 8: Publish & Activate (only after testing passes)
2026-04-09 19:34:48 +08:00
sf agent publish authoring-bundle --json \
2026-03-19 05:17:32 +08:00
--api-name < AgentName > \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
2026-04-09 19:34:48 +08:00
sf agent activate --json \
2026-03-19 05:17:32 +08:00
--api-name < AgentName > \
-o TARGET_ORG
```
Critical notes:
- For **scratch orgs** , use `sf org create user --definition-file`
- For **production/sandbox** , use `sf data create record` as shown above
- `sf org create user` only works in scratch orgs — it will fail in production/sandbox
- Always test with preview BEFORE publishing to avoid version management overhead
- Assign `AgentforceServiceAgentUser` BEFORE publishing to prevent "Internal Error"
- Publishing does NOT activate — you must run `sf agent activate` separately
---
## Service Agent Setup (6 Steps)
### Step 1: Create Einstein Agent User
Service agents need a dedicated service account with consistent permissions.
**Get Org ID first** (needed for username format):
```bash
2026-04-09 19:34:48 +08:00
sf org display --json -o TARGET_ORG
# Read result.id from the JSON response
2026-03-19 05:17:32 +08:00
```
**Query existing Einstein Agent Users** (skip creation if one exists):
```bash
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT Id, Username, IsActive FROM User WHERE Profile.Name = 'Einstein Agent User' AND IsActive = true" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
**Create the user** (if none exists):
1. Get the Einstein Agent User profile ID:
```bash
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT Id FROM Profile WHERE Name = 'Einstein Agent User'" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
2. Create a user definition file (`config/einstein-agent-user.json`):
```json
{
"Username": "{agent_name}_agent@{orgId}.ext",
"LastName": "{AgentName} Agent",
"Email": "placeholder@example.com",
"Alias": "agntuser",
"ProfileId": "< profile-id-from-step-1 > ",
"TimeZoneSidKey": "America/Los_Angeles",
"LocaleSidKey": "en_US",
"EmailEncodingKey": "UTF-8",
"LanguageLocaleKey": "en_US",
"UserPermissionsKnowledgeUser": true
}
```
3. Create the user:
**Option A: Scratch Org (Definition File)**
```bash
2026-04-09 19:34:48 +08:00
sf org create user --json \
2026-03-19 05:17:32 +08:00
--definition-file config/einstein-agent-user.json \
-o TARGET_ORG
```
**Option B: Production/Sandbox (Direct Record Creation)**
```bash
# Get Profile ID first
2026-04-09 19:34:48 +08:00
# Get Profile ID (read result.records[0].Id from JSON response)
sf data query --json \
2026-03-19 05:17:32 +08:00
--query "SELECT Id FROM Profile WHERE Name = 'Einstein Agent User'" \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
2026-04-09 19:34:48 +08:00
# Create user directly (use ProfileId from query above)
sf data create record --json --sobject User --values \
"Username='{agent_name}_agent@{orgId}.ext' LastName='{AgentName} Agent' Email='placeholder@example.com' Alias='agntuser' ProfileId='< PROFILE_ID > ' TimeZoneSidKey='America/Los_Angeles' LocaleSidKey='en_US' EmailEncodingKey='UTF-8' LanguageLocaleKey='en_US'" \
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
**Note** : `sf org create user` only works in scratch orgs. For production/sandbox, use `sf data create record` . Attempting `sf org create user` in a non-scratch org fails with an authorization error.
4. Verify creation:
```bash
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT Id, Username, IsActive FROM User WHERE Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
**Username format**: `{agent_name}_agent@{orgId}.ext` (production) or `{agent_name}.{suffix}@{orgfarm}.salesforce.com` (dev/scratch). Always query the target org to confirm the exact format.
---
### Step 2: Assign System Permission Set (`AgentforceServiceAgentUser`)
Critical: Must be assigned BEFORE publishing the agent. Without it, publish fails with "Internal Error".
Via Setup UI:
1. Setup > Permission Sets > search "AgentforceServiceAgentUser"
2. Manage Assignments > Add Assignments > select the Einstein Agent User > Save
Via CLI:
```bash
2026-04-09 19:34:48 +08:00
sf org assign permset --json --name AgentforceServiceAgentUser --on-behalf-of "{agent_name}_agent@{orgId}.ext" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
Verify assignment:
```bash
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT Id, PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext' AND PermissionSet.Name = 'AgentforceServiceAgentUser'" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
---
### Step 3: Create Custom Permission Set for Apex Classes
The custom PS grants the agent user permission to execute your Apex invocable actions.
Naming convention: `{AgentName}_Access` (e.g., `AutomotiveSupport_Access` )
File: `force-app/main/default/permissionsets/{AgentName}_Access.permissionset-meta.xml`
```xml
<?xml version="1.0" encoding="UTF-8"?>
< PermissionSet xmlns = "http://soap.sforce.com/2006/04/metadata" >
< description > Grants access to {AgentName} Agent Apex classes< / description >
< hasActivationRequired > false< / hasActivationRequired >
< label > {AgentName} Access< / label >
<!-- Add one entry per Apex class the agent calls -->
< classAccesses >
< apexClass > YourApexClassName< / apexClass >
< enabled > true< / enabled >
< / classAccesses >
<!-- Repeat for ALL Apex classes referenced via apex:// in agent script -->
< / PermissionSet >
```
Key rule: Include EVERY Apex class referenced via `apex://` in your agent script. Missing even one causes "invocable action does not exist" at runtime.
Deploy the permission set:
```bash
2026-04-09 19:34:48 +08:00
sf project deploy start --json --source-dir force-app/main/default/permissionsets/{AgentName}_Access.permissionset-meta.xml -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
---
### Step 4: Assign Custom Permission Set to Agent User
Via CLI:
```bash
2026-04-09 19:34:48 +08:00
sf org assign permset --json --name {AgentName}_Access --on-behalf-of "{agent_name}_agent@{orgId}.ext" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
Verify both permission sets are assigned:
```bash
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
Expected output includes both:
- `AgentforceServiceAgentUser` (system)
- `{AgentName}_Access` (custom)
---
### Step 5: Set `default_agent_user` in Agent Config
In your `.agent` file:
```yaml
config:
developer_name: "AgentName"
agent_description: "Your agent description"
agent_type: "AgentforceServiceAgent"
default_agent_user: "{agent_name}_agent@{orgId}.ext" # Service agents ONLY
```
---
### Step 6: Deploy, Test, Publish & Activate
**Validated workflow pattern**: Deploy as unpublished metadata, test with preview, then publish only when tests pass. This avoids version management overhead during iteration.
#### 6.1: Deploy Agent Bundle (Unpublished)
```bash
2026-04-09 19:34:48 +08:00
sf project deploy start --json \
2026-03-19 05:17:32 +08:00
--source-dir force-app/main/default/aiAuthoringBundles/< AgentName > \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
This deploys the agent as **unpublished metadata** — you can edit freely without version management.
#### 6.2: Test with Preview (Before Publishing)
```bash
2026-04-09 19:34:48 +08:00
sf agent preview start --json \
2026-03-19 05:17:32 +08:00
--api-name < AgentName > \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
What to test:
chore: rename topic to subagent for Agent Script v2 @W-21955450@ (#193)
* @W-21955450@ Rename topic to subagent for Agent Script v2
Aligns with Agent Script v2 naming standards where `topic` is renamed
to `subagent` across all skill documentation and templates.
Changes:
- Agent Script templates: topic keyword → subagent keyword
- References: @topic.* → @subagent.*
- Documentation: Updated all skill references and guides
- Natural language references preserved in comments/descriptions
* Rename start_agent topic_selector to agent_router
Completes the topic → subagent terminology alignment by:
1. Renaming start_agent from topic_selector to agent_router (15 agent files)
2. Updating template topic declarations: topic {{placeholder}} → subagent {{placeholder}} (5 files)
3. Updating all @subagent.topic_selector references to @subagent.agent_router (35 occurrences)
4. Updating documentation: prose, examples, and diagrams (10 markdown files)
5. Updating comments to use agent_router terminology
Files affected:
- 22 agent template files
- 10 documentation/reference markdown files
- Template component files
The agent_router name is more descriptive of its actual function
(routing to different subagents) and completes the Agent Script v2
terminology standardization.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Rename files with "topic" to use "subagent" terminology
Completes the topic → subagent terminology alignment by renaming
files and updating all references:
**Files renamed (5):**
- multi-topic.agent → multi-subagent.agent
- template-single-topic.agent → template-single-subagent.agent
- template-multi-topic.agent → template-multi-subagent.agent
- topic-with-actions.agent → subagent-with-actions.agent
- agent-topic-map-diagrams.md → agent-subagent-map-diagrams.md
**References updated (6 docs):**
- Updated all filename references to point to new filenames
- Updated "Topic Map" → "Subagent Map" throughout documentation
- Updated "multi-topic"/"single-topic" → "multi-subagent"/"single-subagent"
Files modified:
- README.md, SKILL.md, agent-spec-template.md
- assets/agents/README.md, assets/README-legacy.md
- references/agent-design-and-spec-creation.md
This ensures consistent "subagent" terminology across filenames,
file content, and all documentation references.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Complete topic-to-subagent terminology update across skills
Comprehensive update replacing "topic" with "subagent" terminology throughout
the developing-agentforce and testing-agentforce skills to align with Agent
Script's `subagent` block naming.
Key changes:
- "Topic Selector" → "Subagent Router" in all agent templates and docs
- "Topic/action" → "Subagent/action" in documentation
- "Topic map" → "Subagent map" in diagram references
- Updated all architecture documentation to use "subagent" terminology
- Updated 19 .agent template files with new labels and comments
- Updated 8 reference documentation files with consistent terminology
API contract preservation:
- Test spec YAML files preserve "topic" terminology to match Testing Center API
- Added clarifying comments explaining topic/subagent equivalence in YAML files
- Field names like `expectedTopic` unchanged (Salesforce API requirement)
Preserved terms:
- "off-topic" (standard phrase for out-of-scope)
- "expectedTopic" field (Testing Center API)
- "platform topics" (Salesforce guardrail features)
32 files changed, 379 insertions(+), 366 deletions(-)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Complete comprehensive topic-to-subagent terminology update
Thorough update replacing all remaining "topic" references with "subagent"
terminology across developing-agentforce, testing-agentforce, and
observing-agentforce skills to fully align with Agent Script's `subagent`
block naming.
Key changes:
- Agent Script syntax: @topic.<name> → @subagent.<name>
- Agent Script syntax: topic.actions → subagent.actions
- Shell script patterns: ^topic → ^subagent
- Documentation: "topic instructions" → "subagent instructions"
- observing-agentforce skill: Updated all agent architecture references
- Template files: Updated all inline comments and descriptions
- Variable names in scripts: TOPIC → SUBAGENT
Specific updates:
- 45 files changed, 294 insertions, 294 deletions
- Updated all Agent Script code examples to use @subagent syntax
- Updated observing-agentforce issue classification guide
- Updated shell script patterns in diagnostic tools
- Updated Apex comments to clarify topic field maps to subagents
Preserved (as required):
- "off-topic" and "off_topic" (standard out-of-scope phrase)
- Testing Center API fields: expectedTopic, topic: in YAML
- API response fields: .topic, generatedData.topic, topic_assertion
- STDM field names: ssot__TopicApiName__c (with clarifying docs)
- Template placeholders in test specs (API values)
- "Topic hash drift" (API field behavior)
- "Email topic/purpose" (means email subject)
- Explanatory comments about API field mapping
All Agent Script syntax and documentation now consistently uses "subagent"
while preserving backward compatibility with platform API field names.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* a few more topic -> subagent replacements
---------
Co-authored-by: Steve Hetzel <shetzel@salesforce.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-28 02:42:18 +08:00
1. All subagents trigger correctly
2026-03-19 05:17:32 +08:00
2. All Apex actions execute without "Insufficient Privileges" errors
3. Agent responds with expected data
4. No compilation errors
If testing reveals problems, edit your agent script or Apex classes, redeploy, and test again — no publish required.
**⚠️ `WITH USER_MODE` Object Permissions:** Apex using `WITH USER_MODE` requires the Einstein Agent User to have read access on queried objects. Class-level access alone is not enough. Missing object permissions fail silently — 0 rows, no error. If live preview returns empty but simulated works, check Setup > Profiles > Einstein Agent User > Object Permissions. Fix by adding `<objectPermissions>` to your custom PS:
```xml
< objectPermissions >
< allowRead > true< / allowRead >
< object > Vehicle__c< / object >
< / objectPermissions >
```
See [preview-test-loop.md ](preview-test-loop.md ) for the complete smoke test workflow.
#### 6.3: Publish Agent
Only publish after all tests pass.
```bash
2026-04-09 19:34:48 +08:00
sf agent publish authoring-bundle --json \
2026-03-19 05:17:32 +08:00
--api-name < AgentName > \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
**Publishing does NOT activate.** The new BotVersion is created as `Inactive` . You must explicitly activate.
#### 6.4: Activate Agent
```bash
2026-04-09 19:34:48 +08:00
sf agent activate --json \
2026-03-19 05:17:32 +08:00
--api-name < AgentName > \
-o TARGET_ORG
```
2026-04-09 19:34:48 +08:00
Note: `sf agent activate` may not support `--json` in all CLI versions. It prints a plain-text confirmation.
2026-03-19 05:17:32 +08:00
#### 6.5: Verify Activation
```bash
2026-04-09 19:34:48 +08:00
sf data query --json \
2026-03-19 05:17:32 +08:00
--query "SELECT Id, DeveloperName, Status FROM BotVersion WHERE BotDefinition.DeveloperName = '< AgentName > ' ORDER BY CreatedDate DESC LIMIT 1" \
2026-04-09 19:34:48 +08:00
-o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
Expected: `Status = 'Active'`
After publish: Any further changes require version management. Test thoroughly before publishing.
---
## Employee Agent Setup
Employee agents run as the logged-in user. The permission model is simpler.
### What You DO NOT Need
- No Einstein Agent User creation
- No `AgentforceServiceAgentUser` system permission set
- No `default_agent_user` in agent config
### What You DO Need
Custom permission set(s) assigned to **employees** who will use the agent.
### Step 1: Create Custom Permission Set
Same XML template as Step 3 above. Include `<classAccesses>` for all Apex classes the agent calls.
### Step 2: Assign to Employees
Assign the custom PS to employees (not to a service account):
```bash
2026-04-09 19:34:48 +08:00
sf org assign permset --json --name {AgentName}_Access --on-behalf-of "employee@company.com" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
Or use Permission Set Groups for role-based access.
### Step 3: Configure Agent Script (No `default_agent_user`)
```yaml
config:
developer_name: "Employee_Agent"
agent_description: "Internal employee assistant"
agent_type: "AgentforceEmployeeAgent"
# NO default_agent_user — agent runs as logged-in user
```
### Step 4: Publish
```bash
2026-04-09 19:34:48 +08:00
sf agent publish authoring-bundle --json --api-name Employee_Agent -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
---
## Auto-Generated Permission Set Warning
Salesforce auto-generates `NextGen_{AgentName}_Permissions` when an agent is published. Do NOT rely on this PS — it is often incomplete.
### ORM1 Testing Example
- Agent script referenced 4 Apex classes: `OrderManagementVerification` , `FraudRiskCalculator` , `OrderLookupService` , `ShipmentTracker`
- Auto-generated `NextGen_ORM1_Permissions` only included 3 classes (missing `ShipmentTracker` )
- Runtime error: "invocable action track_delivery does not exist"
- Fix: Created custom `ORM1_Access` with all 4 classes — no errors
Best practice: Always create your own custom `{AgentName}_Access` PS with explicit `<classAccesses>` for every Apex class. Ignore the auto-generated PS.
---
## End-to-End Verification Checklist
Run this combined query to verify all setup steps for a Service Agent:
```bash
# 1. Einstein Agent User exists and is active
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT Id, Username, IsActive, Profile.Name FROM User WHERE Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
# 2. System PS assigned
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext' AND PermissionSet.Name = 'AgentforceServiceAgentUser'" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
# 3. Custom PS assigned
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext' AND PermissionSet.Name = '{AgentName}_Access'" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
# 4. All permission sets for user (combined view)
2026-04-09 19:34:48 +08:00
sf data query --json --query "SELECT PermissionSet.Name, PermissionSet.Label FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
2026-03-19 05:17:32 +08:00
# 5. Agent config has default_agent_user
# Check your .agent file's config: block
# 6. Agent publishes successfully
2026-04-09 19:34:48 +08:00
sf agent publish authoring-bundle --json --api-name AgentName -o TARGET_ORG
2026-03-19 05:17:32 +08:00
```
Checklist:
- [ ] Einstein Agent User created and active (`IsActive = true`)
- [ ] Profile is "Einstein Agent User" (or "Minimum Access - Salesforce")
- [ ] `AgentforceServiceAgentUser` system PS assigned
- [ ] Custom `{AgentName}_Access` PS deployed with ALL Apex classes
- [ ] Custom PS assigned to the agent user
- [ ] `default_agent_user` set in `.agent` config block
- [ ] Agent tested with preview before publishing
- [ ] Agent publishes without error
- [ ] Agent activated (publish does NOT auto-activate)
---
## Common Pitfalls (Validated)
### 1. "Internal Error" on First Publish
- **Cause:** Publishing before assigning `AgentforceServiceAgentUser`
- **Prevention:** Assign system PS (Step 2) before publishing (Step 6.3)
- **Result:** First-time publish success (no retries needed)
### 2. "Insufficient Privileges" on Apex Actions
- **Cause:** Missing `<classAccesses>` in custom permission set
- **Prevention:** Custom PS template includes all Apex classes (Step 3)
- **Result:** All actions execute without permission errors
### 3. Testing After Publishing
- **Cause:** Publishing before testing, then needing version management for fixes
- **Prevention:** Deploy → Test → Publish workflow (Step 6.1-6.3)
- **Result:** No version management overhead during development
### 4. Wrong User Creation Command
- **Cause:** Using `sf org create user` in non-scratch orgs
- **Prevention:** Step 1 provides correct commands for each org type (Option A vs B)
- **Result:** User created successfully without authorization errors
### 5. Auto-Generated Permission Set Gaps
- **Cause:** Relying on `NextGen_{AgentName}_Permissions` (often incomplete)
- **Prevention:** Custom PS with explicit Apex access (Step 3)
- **Result:** All Apex classes accessible from the start
### 6. Forgot to Activate After Publish
- **Cause:** Assuming publish automatically activates
- **Prevention:** Step 6 splits publish and activate into separate steps with verification
- **Result:** Agent is both published AND activated
---
## Troubleshooting
| Error | Cause | Fix |
|-------|-------|-----|
| "Internal Error" on publish | `AgentforceServiceAgentUser` PS not assigned to Einstein Agent User | Assign system PS (Step 2), wait 2-3 min, retry publish |
| "Insufficient Privileges" at runtime | Custom PS missing or incomplete `<classAccesses>` | Verify custom PS includes ALL Apex classes, redeploy + reassign |
| "invocable action does not exist" | Apex class not in custom PS (auto-generated PS incomplete) | Create custom `{AgentName}_Access` with all `<classAccesses>` (Step 3) |
| "Invalid default_agent_user" | Username typo or user not active | Query Einstein Agent Users, verify exact username + `IsActive = true` |
| Agent runs but returns wrong data | Employee agent using wrong user context | Verify `agent_type` — Service agents use dedicated user, Employee agents use logged-in user |
| `sf org create user` fails | Used in production/sandbox org | Use `sf data create record` instead (Step 1, Option B) |
---
## Permission Set XML Template (Complete Example)
**AutomotiveSupport agent** (5 Apex classes):
```xml
<?xml version="1.0" encoding="UTF-8"?>
< PermissionSet xmlns = "http://soap.sforce.com/2006/04/metadata" >
< description > Grants access to Automotive Support Agent Apex classes< / description >
< hasActivationRequired > false< / hasActivationRequired >
< label > Automotive Support Access< / label >
< classAccesses >
< apexClass > VehicleLookupService< / apexClass >
< enabled > true< / enabled >
< / classAccesses >
< classAccesses >
< apexClass > ErrorCodeDiagnosticsService< / apexClass >
< enabled > true< / enabled >
< / classAccesses >
< classAccesses >
< apexClass > CheckEngineDiagnosticsService< / apexClass >
< enabled > true< / enabled >
< / classAccesses >
< classAccesses >
< apexClass > BehaviorAnalysisService< / apexClass >
< enabled > true< / enabled >
< / classAccesses >
< classAccesses >
< apexClass > ServiceSchedulerService< / apexClass >
< enabled > true< / enabled >
< / classAccesses >
< / PermissionSet >
```
---
*Validated against: ORM1, ORM2, AutomotiveSupport, SalesforceProductAssistant agents. Last validated: 2026-03-07.*