* @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>
12 KiB
Production Gotchas: Billing, Determinism & Performance
Credit consumption, lifecycle hooks, determinism patterns, and performance guardrails in Agentforce.
Credit Consumption Table
| Operation | Credits | Notes |
|---|---|---|
@utils.transition |
FREE | Framework navigation |
@utils.setVariables |
FREE | Framework state management |
@utils.escalate |
FREE | Framework escalation |
if/else control flow |
FREE | Deterministic resolution |
before_reasoning |
FREE | Deterministic pre-processing (see note below) |
after_reasoning |
FREE | Deterministic post-processing (see note below) |
reasoning (LLM turn) |
FREE | LLM reasoning itself is not billed |
| Prompt Templates | 2-16 | Per invocation (varies by complexity) |
| Flow actions | 20 | Per action execution |
| Apex actions | 20 | Per action execution |
| Any other action | 20 | Per action execution |
The before_reasoning: and after_reasoning: lifecycle hooks are validated. Content goes directly under the block (no instructions: wrapper). See "Lifecycle Hooks" section below for correct syntax.
Cost Optimization Pattern
Fetch data once in before_reasoning:, cache in variables, reuse across subagents.
Lifecycle Hooks
subagent main:
description: "Subagent with lifecycle hooks"
# BEFORE: Runs deterministically BEFORE LLM sees instructions
before_reasoning:
# Content goes DIRECTLY here (NO instructions: wrapper!)
set @variables.pre_processed = True
set @variables.customer_tier = "gold"
# LLM reasoning phase
reasoning:
instructions: ->
| Customer tier: {!@variables.customer_tier}
| How can I help you today?
# AFTER: Runs deterministically AFTER LLM finishes reasoning
after_reasoning:
# Content goes DIRECTLY here (NO instructions: wrapper!)
set @variables.interaction_logged = True
if @variables.needs_audit == True:
set @variables.audit_flag = True
Key Points:
- Content goes directly under
before_reasoning:/after_reasoning:(NOinstructions:wrapper) - Reliable primitives:
set,if/else,transition to.runhas inconsistent runtime behavior across bundle types — use it inreasoning.actions:orinstructions: ->instead before_reasoning:is FREE (no credit cost) - use for data prepafter_reasoning:is FREE (no credit cost) - use for logging, cleanuptransition toworks inafter_reasoning:— but if a subagent transitions mid-reasoning, the original subagent'safter_reasoning:does NOT run
❌ WRONG Syntax (causes compile error):
before_reasoning:
instructions: -> # ❌ NO! Don't wrap with instructions:
set @variables.x = True
✅ CORRECT Syntax:
before_reasoning:
set @variables.x = True # ✅ Direct content under the block
Supervision vs Handoff
| Term | Syntax | Behavior | Use When |
|---|---|---|---|
| Handoff | @utils.transition to @subagent.X |
Control transfers completely, child generates final response | Checkout, escalation, terminal states |
| Supervision | @subagent.X (as action reference) |
Parent orchestrates, child returns, parent synthesizes | Expert consultation, sub-tasks |
# HANDOFF - child subagent takes over completely:
checkout: @utils.transition to @subagent.order_checkout
description: "Proceed to checkout"
# → @subagent.order_checkout generates the user-facing response
# SUPERVISION - parent remains in control:
get_advice: @subagent.product_expert
description: "Consult product expert"
# → @subagent.product_expert returns, parent subagent synthesizes final response
KNOWN BUG: Adding ANY new action in Canvas view may inadvertently change Supervision references to Handoff transitions.
Action Output Flags for Zero-Hallucination Routing
Control what the LLM can see and say.
When defining actions in Agentforce Assets, use these output flags:
| Flag | Effect | Use When |
|---|---|---|
filter_from_agent: True |
LLM cannot show this value to user | Preventing hallucinated responses (GA standard) |
is_used_by_planner: True |
LLM can reason about this value | Decision-making, routing |
Zero-Hallucination Intent Classification Pattern:
# In Agentforce Assets - Action Definition outputs:
outputs:
intent_classification: string
filter_from_agent: True # LLM cannot show this to user (GA standard)
is_used_by_planner: True # LLM can use for routing decisions
# In Agent Script - LLM routes but cannot hallucinate:
subagent intent_router:
reasoning:
instructions: ->
run @actions.classify_intent
set @variables.intent = @outputs.intent_classification
if @variables.intent == "refund":
transition to @subagent.refunds
if @variables.intent == "order_status":
transition to @subagent.orders
Action I/O Metadata Properties
Complete reference for all metadata properties available on action definitions, inputs, and outputs.
Action-Level Properties:
| Property | Type | Effect |
|---|---|---|
label |
String | Display name in UI |
description |
String | LLM reads this for decision-making |
require_user_confirmation |
Boolean | Request user confirmation before execution (compiles; runtime no-op per Issue 6) |
include_in_progress_indicator |
Boolean | Show spinner during execution |
progress_indicator_message |
String | Custom spinner text |
Input Properties:
| Property | Type | Effect |
|---|---|---|
description |
String | Explains parameter to LLM |
label |
String | Display name in UI |
is_required |
Boolean | Marks input as mandatory for LLM |
is_user_input |
Boolean | LLM extracts value from conversation |
complex_data_type_name |
String | Lightning type mapping |
Output Properties:
| Property | Type | Effect |
|---|---|---|
description |
String | Explains output to LLM |
label |
String | Display name in UI |
filter_from_agent |
Boolean | True = hide from user display (GA standard) |
is_displayable |
Boolean | False = hide from user (compile-valid alias) |
is_used_by_planner |
Boolean | True = LLM can reason about value |
developer_name |
String | Overrides the parameter's developer name |
complex_data_type_name |
String | Lightning type mapping |
filter_from_agent: True is the GA standard name. is_displayable: False is a compile-valid alias.
User Input Pattern
With is_user_input: True:
inputs:
customer_name: string
description: "Customer's full name"
is_user_input: True # LLM pulls from what user already said
is_required: True # Must have a value before action executes
Action Chaining with run Keyword
Parent action may complain about inputs needed by chained action - this is expected.
process_order: @actions.create_order
with customer_id = @variables.customer_id
run @actions.send_confirmation # Chains after create_order completes
set @variables.order_id = @outputs.id
KNOWN BUG: Chained actions with Prompt Templates don't properly map inputs using Input:Query format.
For prompt template action definitions, input binding syntax, and grounded data patterns, see Action Prompt Templates.
Latch Variable Pattern for Subagent Re-entry
Subagent router doesn't properly re-evaluate after user provides missing input. Use a "latch" variable to force re-entry:
variables:
verification_in_progress: mutable boolean = False
start_agent agent_router:
reasoning:
instructions: ->
if @variables.verification_in_progress == True:
transition to @subagent.verification
| How can I help you today?
actions:
start_verify: @subagent.verification
description: "Start identity verification"
set @variables.verification_in_progress = True
subagent verification:
reasoning:
instructions: ->
| Please provide your email to verify your identity.
actions:
verify: @actions.verify_identity
with email = ...
set @variables.verified = @outputs.success
set @variables.verification_in_progress = False
Loop Protection Guardrail
Agent Scripts have a built-in guardrail that limits iterations to approximately 3-4 loops before breaking out and returning to the Subagent Router.
Best Practice: Map out your execution paths and test for unintended circular references between subagents.
Token & Size Limits
| Limit Type | Value | Notes |
|---|---|---|
| Max response size | 1,048,576 bytes (1MB) | Per agent response |
| Plan trace limit (Frontend) | 1M characters | For debugging UI |
| Transformed plan trace (Backend) | 32k tokens | Internal processing |
| Active/Committed Agents per org | 100 max | Org limit |
Progress Indicators
actions:
fetch_data: @actions.get_customer_data
description: "Fetch customer information"
include_in_progress_indicator: True
progress_indicator_message: "Fetching your account details..."
VS Code Pull/Push NOT Supported
# ❌ ERROR when using source tracking:
Failed to retrieve components using source tracking:
[SfError [UnsupportedBundleTypeError]: Unsupported Bundle Type: AiAuthoringBundle
# ✅ WORKAROUND - Use CLI directly:
sf project retrieve start --json -m AiAuthoringBundle:MyAgent
sf agent publish authoring-bundle --json --api-name MyAgent -o TARGET_ORG
@inputs Scope Lifecycle (Silent Failure)
@inputs is only available in with directives during action invocation. Using @inputs in a post-action set causes silent runtime failure — the action executes but the set silently drops, leaving the variable unchanged. No trace error; the FunctionStep shows no output capture.
# WRONG — silent failure, @inputs out of scope after action executes
run @actions.get_station_status
with station_name = ...
set @variables.station = @inputs.station_name # FAILS SILENTLY
# RIGHT — use @outputs (if action echoes the value) or capture input before the call
set @variables.station = @variables.selected_station # capture before
run @actions.get_station_status
with station_name = @variables.station
set @variables.status = @outputs.status # @outputs is valid here
Diagnosis: A FunctionStep that completes with no output capture (set directives dropped) indicates an @inputs scope violation. The action succeeds — only the assignment fails.
Similarly, @outputs is only available in set and if directives immediately following the action invocation — not in instructions, pipe lines, or later actions.
Reserved @InvocableVariable Keywords
Certain common words cannot be used as @InvocableVariable names in Apex classes called by Agent Script. Using them causes "SyntaxError: Unexpected '{keyword}'" during agent script compilation. (Validated March 2026)
Reserved names (cannot use as @InvocableVariable):
| Reserved Name | Workaround | Example |
|---|---|---|
model |
vehicle_model, data_model, model_name |
@InvocableVariable public String vehicle_model; |
description |
issue_description, desc_text, description_field |
@InvocableVariable public String issue_description; |
label |
label_text, display_label, label_field |
@InvocableVariable public String label_text; |
How it manifests:
- Apex compiles and deploys successfully (these are valid Apex identifiers)
- Error only appears when the Agent Script compiler processes the action's I/O schema
- Error message:
SyntaxError: Unexpected 'model'(ordescription,label) - Fix: Rename the
@InvocableVariablein Apex, redeploy, then republish the agent
Language Block Quirks
- Hebrew and Indonesian appear twice in the language dropdown
- Selecting from the second set causes save errors
- Use
adaptive_response_allowed: Truefor automatic language adaptation