* @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>
19 KiB
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) |
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
CLI Fast Track: Complete Workflow
For CLI-first workflow (tested: ~8 minutes total):
# Step 1: Query existing Einstein Agent Users (30 seconds)
sf data query --json \
--query "SELECT Id, Username, IsActive FROM User WHERE Profile.Name = 'Einstein Agent User' AND IsActive = true" \
-o TARGET_ORG
# Step 2: Create Einstein Agent User (2 minutes)
# Get Profile ID (read result.records[0].Id from JSON response)
sf data query --json \
--query "SELECT Id FROM Profile WHERE Name = 'Einstein Agent User'" \
-o TARGET_ORG
# For Production/Sandbox (non-scratch org):
# Use the ProfileId from the query above
sf data create record --json --sobject User --values \
"Username=<agent_name>_user@<orgId>.ext \
LastName=<AgentName> \
Email=admin@example.com \
Alias=<alias> \
TimeZoneSidKey=America/Los_Angeles \
LocaleSidKey=en_US \
EmailEncodingKey=UTF-8 \
ProfileId=<PROFILE_ID> \
LanguageLocaleKey=en_US" \
-o TARGET_ORG
# 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)
sf org assign permset --json \
--name AgentforceServiceAgentUser \
--on-behalf-of <agent_name>_user@<orgId>.ext \
-o TARGET_ORG
# Step 4: Deploy Custom Permission Set (3 minutes)
# (Create the .permissionset-meta.xml file first - see Section 3.2 template)
sf project deploy start --json \
--metadata PermissionSet:<AgentName>_Access \
-o TARGET_ORG
# Assign custom PS
sf org assign permset --json \
--name <AgentName>_Access \
--on-behalf-of <agent_name>_user@<orgId>.ext \
-o TARGET_ORG
# Step 5: Verify All Permissions (1 minute)
sf data query --json \
--query "SELECT PermissionSet.Name, PermissionSet.Label FROM PermissionSetAssignment WHERE Assignee.Username = '<agent_name>_user@<orgId>.ext' ORDER BY PermissionSet.Name" \
-o TARGET_ORG
# Expected: AgentforceServiceAgentUser + <AgentName>_Access
# Step 6: Deploy Agent Bundle (unpublished metadata)
sf project deploy start --json \
--source-dir force-app/main/default/aiAuthoringBundles/<AgentName> \
-o TARGET_ORG
# Step 7: Test BEFORE Publishing (recommended)
sf agent preview start --json \
--api-name <AgentName> \
-o TARGET_ORG
# Test all subagents and actions to verify permissions
# Step 8: Publish & Activate (only after testing passes)
sf agent publish authoring-bundle --json \
--api-name <AgentName> \
-o TARGET_ORG
sf agent activate --json \
--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 recordas shown above sf org create useronly works in scratch orgs — it will fail in production/sandbox- Always test with preview BEFORE publishing to avoid version management overhead
- Assign
AgentforceServiceAgentUserBEFORE publishing to prevent "Internal Error" - Publishing does NOT activate — you must run
sf agent activateseparately
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):
sf org display --json -o TARGET_ORG
# Read result.id from the JSON response
Query existing Einstein Agent Users (skip creation if one exists):
sf data query --json --query "SELECT Id, Username, IsActive FROM User WHERE Profile.Name = 'Einstein Agent User' AND IsActive = true" -o TARGET_ORG
Create the user (if none exists):
-
Get the Einstein Agent User profile ID:
sf data query --json --query "SELECT Id FROM Profile WHERE Name = 'Einstein Agent User'" -o TARGET_ORG -
Create a user definition file (
config/einstein-agent-user.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 } -
Create the user:
Option A: Scratch Org (Definition File)
sf org create user --json \ --definition-file config/einstein-agent-user.json \ -o TARGET_ORGOption B: Production/Sandbox (Direct Record Creation)
# Get Profile ID first # Get Profile ID (read result.records[0].Id from JSON response) sf data query --json \ --query "SELECT Id FROM Profile WHERE Name = 'Einstein Agent User'" \ -o TARGET_ORG # 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_ORGNote:
sf org create useronly works in scratch orgs. For production/sandbox, usesf data create record. Attemptingsf org create userin a non-scratch org fails with an authorization error. -
Verify creation:
sf data query --json --query "SELECT Id, Username, IsActive FROM User WHERE Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
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:
- Setup > Permission Sets > search "AgentforceServiceAgentUser"
- Manage Assignments > Add Assignments > select the Einstein Agent User > Save
Via CLI:
sf org assign permset --json --name AgentforceServiceAgentUser --on-behalf-of "{agent_name}_agent@{orgId}.ext" -o TARGET_ORG
Verify assignment:
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
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 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:
sf project deploy start --json --source-dir force-app/main/default/permissionsets/{AgentName}_Access.permissionset-meta.xml -o TARGET_ORG
Step 4: Assign Custom Permission Set to Agent User
Via CLI:
sf org assign permset --json --name {AgentName}_Access --on-behalf-of "{agent_name}_agent@{orgId}.ext" -o TARGET_ORG
Verify both permission sets are assigned:
sf data query --json --query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
Expected output includes both:
AgentforceServiceAgentUser(system){AgentName}_Access(custom)
Step 5: Set default_agent_user in Agent Config
In your .agent file:
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)
sf project deploy start --json \
--source-dir force-app/main/default/aiAuthoringBundles/<AgentName> \
-o TARGET_ORG
This deploys the agent as unpublished metadata — you can edit freely without version management.
6.2: Test with Preview (Before Publishing)
sf agent preview start --json \
--api-name <AgentName> \
-o TARGET_ORG
What to test:
- All subagents trigger correctly
- All Apex actions execute without "Insufficient Privileges" errors
- Agent responds with expected data
- 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:
<objectPermissions>
<allowRead>true</allowRead>
<object>Vehicle__c</object>
</objectPermissions>
See preview-test-loop.md for the complete smoke test workflow.
6.3: Publish Agent
Only publish after all tests pass.
sf agent publish authoring-bundle --json \
--api-name <AgentName> \
-o TARGET_ORG
Publishing does NOT activate. The new BotVersion is created as Inactive. You must explicitly activate.
6.4: Activate Agent
sf agent activate --json \
--api-name <AgentName> \
-o TARGET_ORG
Note: sf agent activate may not support --json in all CLI versions. It prints a plain-text confirmation.
6.5: Verify Activation
sf data query --json \
--query "SELECT Id, DeveloperName, Status FROM BotVersion WHERE BotDefinition.DeveloperName = '<AgentName>' ORDER BY CreatedDate DESC LIMIT 1" \
-o TARGET_ORG
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
AgentforceServiceAgentUsersystem permission set - No
default_agent_userin 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):
sf org assign permset --json --name {AgentName}_Access --on-behalf-of "employee@company.com" -o TARGET_ORG
Or use Permission Set Groups for role-based access.
Step 3: Configure Agent Script (No default_agent_user)
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
sf agent publish authoring-bundle --json --api-name Employee_Agent -o TARGET_ORG
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_Permissionsonly included 3 classes (missingShipmentTracker) - Runtime error: "invocable action track_delivery does not exist"
- Fix: Created custom
ORM1_Accesswith 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:
# 1. Einstein Agent User exists and is active
sf data query --json --query "SELECT Id, Username, IsActive, Profile.Name FROM User WHERE Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
# 2. System PS assigned
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
# 3. Custom PS assigned
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
# 4. All permission sets for user (combined view)
sf data query --json --query "SELECT PermissionSet.Name, PermissionSet.Label FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
# 5. Agent config has default_agent_user
# Check your .agent file's config: block
# 6. Agent publishes successfully
sf agent publish authoring-bundle --json --api-name AgentName -o TARGET_ORG
Checklist:
- Einstein Agent User created and active (
IsActive = true) - Profile is "Einstein Agent User" (or "Minimum Access - Salesforce")
AgentforceServiceAgentUsersystem PS assigned- Custom
{AgentName}_AccessPS deployed with ALL Apex classes - Custom PS assigned to the agent user
default_agent_userset in.agentconfig 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 userin 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 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.