@salesforce/afv-skills 1.1.0 → 1.2.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.
Files changed (103) hide show
  1. package/package.json +4 -4
  2. package/skills/agentforce-development/SKILL.md +427 -0
  3. package/skills/agentforce-development/assets/README-legacy.md +89 -0
  4. package/skills/agentforce-development/assets/agent-spec-template.md +90 -0
  5. package/skills/agentforce-development/assets/agents/README.md +45 -0
  6. package/skills/agentforce-development/assets/agents/hello-world.agent +60 -0
  7. package/skills/agentforce-development/assets/agents/multi-topic.agent +105 -0
  8. package/skills/agentforce-development/assets/agents/production-faq.agent +101 -0
  9. package/skills/agentforce-development/assets/agents/production-faq.bundle-meta.xml +4 -0
  10. package/skills/agentforce-development/assets/agents/simple-qa.agent +72 -0
  11. package/skills/agentforce-development/assets/apex/models-api-queueable.cls +225 -0
  12. package/skills/agentforce-development/assets/bundle-meta.xml +23 -0
  13. package/skills/agentforce-development/assets/components/apex-action.agent +52 -0
  14. package/skills/agentforce-development/assets/components/error-handling.agent +58 -0
  15. package/skills/agentforce-development/assets/components/escalation-setup.agent +169 -0
  16. package/skills/agentforce-development/assets/components/flow-action.agent +66 -0
  17. package/skills/agentforce-development/assets/components/n-ary-conditions.agent +110 -0
  18. package/skills/agentforce-development/assets/components/topic-with-actions.agent +40 -0
  19. package/skills/agentforce-development/assets/deterministic-routing.agent +166 -0
  20. package/skills/agentforce-development/assets/escalation-pattern.agent +209 -0
  21. package/skills/agentforce-development/assets/flow-action-lookup.agent +115 -0
  22. package/skills/agentforce-development/assets/hub-and-spoke.agent +104 -0
  23. package/skills/agentforce-development/assets/invocable-apex-template.cls +187 -0
  24. package/skills/agentforce-development/assets/local-info-agent-annotated.agent +355 -0
  25. package/skills/agentforce-development/assets/metadata/basic-prompt-template.promptTemplate-meta.xml +109 -0
  26. package/skills/agentforce-development/assets/metadata/genai-function-apex.xml +92 -0
  27. package/skills/agentforce-development/assets/metadata/genai-function-flow.xml +57 -0
  28. package/skills/agentforce-development/assets/metadata/genai-plugin.xml +72 -0
  29. package/skills/agentforce-development/assets/metadata/http-callout-flow.flow-meta.xml +348 -0
  30. package/skills/agentforce-development/assets/metadata/record-grounded-prompt.promptTemplate-meta.xml +136 -0
  31. package/skills/agentforce-development/assets/minimal-starter.agent +42 -0
  32. package/skills/agentforce-development/assets/patterns/README.md +254 -0
  33. package/skills/agentforce-development/assets/patterns/action-callbacks.agent +178 -0
  34. package/skills/agentforce-development/assets/patterns/advanced-input-bindings.agent +141 -0
  35. package/skills/agentforce-development/assets/patterns/bidirectional-routing.agent +156 -0
  36. package/skills/agentforce-development/assets/patterns/critical-input-collection.agent +244 -0
  37. package/skills/agentforce-development/assets/patterns/delegation-routing.agent +89 -0
  38. package/skills/agentforce-development/assets/patterns/lifecycle-events.agent +127 -0
  39. package/skills/agentforce-development/assets/patterns/llm-controlled-actions.agent +184 -0
  40. package/skills/agentforce-development/assets/patterns/multi-step-workflow.agent +282 -0
  41. package/skills/agentforce-development/assets/patterns/open-gate-routing.agent +286 -0
  42. package/skills/agentforce-development/assets/patterns/procedural-instructions.agent +273 -0
  43. package/skills/agentforce-development/assets/patterns/prompt-template-action.agent +188 -0
  44. package/skills/agentforce-development/assets/patterns/system-instruction-overrides.agent +293 -0
  45. package/skills/agentforce-development/assets/prompt-rag-search.agent +131 -0
  46. package/skills/agentforce-development/assets/template-multi-topic.agent +160 -0
  47. package/skills/agentforce-development/assets/template-single-topic.agent +81 -0
  48. package/skills/agentforce-development/assets/verification-gate.agent +208 -0
  49. package/skills/agentforce-development/references/action-prompt-templates.md +164 -0
  50. package/skills/agentforce-development/references/actions-reference.md +592 -0
  51. package/skills/agentforce-development/references/agent-access-guide.md +72 -0
  52. package/skills/agentforce-development/references/agent-design-and-spec-creation.md +1010 -0
  53. package/skills/agentforce-development/references/agent-metadata-and-lifecycle.md +575 -0
  54. package/skills/agentforce-development/references/agent-script-core-language.md +1218 -0
  55. package/skills/agentforce-development/references/agent-topic-map-diagrams.md +323 -0
  56. package/skills/agentforce-development/references/agent-user-setup.md +526 -0
  57. package/skills/agentforce-development/references/agent-validation-and-debugging.md +803 -0
  58. package/skills/agentforce-development/references/known-issues.md +353 -0
  59. package/skills/agentforce-development/references/minimal-examples.md +67 -0
  60. package/skills/agentforce-development/references/production-gotchas.md +279 -0
  61. package/skills/agentforce-development/references/salesforce-cli-for-agents.md +393 -0
  62. package/skills/agentforce-development/references/version-history.md +23 -0
  63. package/skills/generate-permission-set/SKILL.md +174 -0
  64. package/skills/salesforce-custom-application/SKILL.md +1 -2
  65. package/skills/salesforce-custom-field/SKILL.md +0 -4
  66. package/skills/salesforce-custom-tab/SKILL.md +84 -8
  67. package/skills/salesforce-experience-lwr-site/SKILL.md +196 -0
  68. package/skills/salesforce-experience-lwr-site/docs/bootstrap-template-byo-lwr.md +224 -0
  69. package/skills/salesforce-experience-lwr-site/docs/configure-content-brandingSet.md +131 -0
  70. package/skills/salesforce-experience-lwr-site/docs/configure-content-route.md +232 -0
  71. package/skills/salesforce-experience-lwr-site/docs/configure-content-themeLayout.md +141 -0
  72. package/skills/salesforce-experience-lwr-site/docs/configure-content-view.md +233 -0
  73. package/skills/salesforce-experience-lwr-site/docs/configure-guest-sharing-rules.md +42 -0
  74. package/skills/salesforce-experience-lwr-site/docs/handle-component-and-region-ids.md +27 -0
  75. package/skills/salesforce-experience-lwr-site/docs/handle-ui-components.md +215 -0
  76. package/skills/salesforce-flow/SKILL.md +2 -2
  77. package/skills/salesforce-fragment/SKILL.md +85 -10
  78. package/skills/salesforce-lightning-app-build/SKILL.md +102 -10
  79. package/skills/apex-class/SKILL.md +0 -253
  80. package/skills/apex-class/examples/AccountDeduplicationBatch.cls +0 -148
  81. package/skills/apex-class/examples/AccountSelector.cls +0 -193
  82. package/skills/apex-class/examples/AccountService.cls +0 -201
  83. package/skills/apex-class/templates/abstract.cls +0 -128
  84. package/skills/apex-class/templates/batch.cls +0 -125
  85. package/skills/apex-class/templates/domain.cls +0 -102
  86. package/skills/apex-class/templates/dto.cls +0 -108
  87. package/skills/apex-class/templates/exception.cls +0 -51
  88. package/skills/apex-class/templates/interface.cls +0 -25
  89. package/skills/apex-class/templates/queueable.cls +0 -92
  90. package/skills/apex-class/templates/schedulable.cls +0 -75
  91. package/skills/apex-class/templates/selector.cls +0 -92
  92. package/skills/apex-class/templates/service.cls +0 -69
  93. package/skills/apex-class/templates/utility.cls +0 -97
  94. package/skills/apex-test-class/SKILL.md +0 -101
  95. package/skills/apex-test-class/references/assertion-patterns.md +0 -209
  96. package/skills/apex-test-class/references/async-testing.md +0 -276
  97. package/skills/apex-test-class/references/mocking-patterns.md +0 -219
  98. package/skills/apex-test-class/references/test-data-factory.md +0 -176
  99. package/skills/deployment-readiness-check/SKILL.md +0 -257
  100. package/skills/deployment-readiness-check/assets/deployment_checklist.md +0 -286
  101. package/skills/deployment-readiness-check/references/rollback_procedures.md +0 -308
  102. package/skills/deployment-readiness-check/scripts/check_metadata.sh +0 -207
  103. package/skills/salesforce-experience-site/SKILL.md +0 -178
@@ -0,0 +1,1010 @@
1
+ # Agent Design and Spec Creation
2
+
3
+ ## Table of Contents
4
+
5
+ 1. Agent Spec: Structure and Lifecycle
6
+ 2. Discovery Questions
7
+ 3. Environment Prerequisites
8
+ 4. Topic Architecture
9
+ 5. Mapping Logic to Actions
10
+ 6. Transition Patterns
11
+ 7. Deterministic vs. Subjective Flow Control
12
+ 8. Gating Patterns
13
+ 9. Action Loop Prevention
14
+
15
+ ---
16
+
17
+ ## 1. Agent Spec: Structure and Lifecycle
18
+
19
+ An **Agent Spec** is a structured design document describing an agent's purpose, topics, actions, state, control flow, and behavioral intent. When creating a new agent, produce the Agent Spec before writing Agent Script code. When comprehending or diagnosing an existing agent, reverse-engineer an Agent Spec from the `.agent` file to make the agent's design explicit.
20
+
21
+ ### What an Agent Spec Contains
22
+
23
+ - **Purpose & Scope** — what the agent does, in plain language
24
+ - **Behavioral Intent** — what the agent is supposed to achieve (requirements and constraints), not just what the code does
25
+ - **Topic Map** — a Mermaid flowchart showing all topics, transitions (with type labels: handoff or delegation), and when transitions occur
26
+ - **Actions & Backing Logic** — each action's name, its backing implementation (Apex class, Flow, Prompt Template), inputs/outputs with visibility decisions, and whether the backing logic exists or needs creation
27
+ - **Variables** — declarations, types, default values, which topics set/read them, and what gates they control
28
+ - **Gating Logic** — conditions that govern action visibility or instruction evaluation, with rationale for each. Always include this section; if no gating applies, state "No gating required" so reviewers know it was considered, not overlooked.
29
+
30
+ ### Directional vs. Observational Entries
31
+
32
+ Agent Spec entries can be directional or observational — both are valid:
33
+
34
+ - **Directional:** "The `confirm_booking` action needs an Apex class `BookingConfirmer` that accepts reservation_id (string), guest_name (string), and returns confirmation_number (string), booking_date (date)." This is a gap you're creating acceptance criteria for.
35
+
36
+ - **Observational:** "The `fetch_weather` action is backed by Apex class `WeatherService`, invoked via `apex://WeatherService`. Accepts dateToCheck (date), returns maxTemp/minTemp (number)." This documents existing backing logic.
37
+
38
+ Both go in the same Agent Spec section.
39
+
40
+ ### Lifecycle Stages
41
+
42
+ The Agent Spec evolves across the agent's lifecycle:
43
+
44
+ **Creation (sparse).** Purpose, topic names, rough descriptions, directional notes about backing logic ("this action needs an Apex class that accepts X, returns Y"). No flowchart yet. Entries are mostly placeholders.
45
+
46
+ **Build (filled).** Flowchart added with transition types labeled. Backing logic mapped (existing implementations identified with filenames, missing implementations stubbed with protocols and I/O specs). Variables documented with their usage and gating impact. Gating rationale explained.
47
+
48
+ **Comprehension (reverse-engineered).** Starting from an existing `.agent` file, produce a complete Agent Spec by parsing topics, tracing transitions, analyzing actions, and documenting state. This is the "what does this agent do?" output.
49
+
50
+ **Diagnosis (reference).** Compare actual runtime behavior against the Agent Spec to find where intent and implementation diverge.
51
+
52
+ ### Agent Spec Template
53
+
54
+ Use the starter spec template at `assets/agent-spec-template.md` for new agents.
55
+
56
+ ---
57
+
58
+ ## 2. Discovery Questions
59
+
60
+ These five question categories drive the content of your Agent Spec. When creating a new agent, use them to elicit requirements from the human. When comprehending or diagnosing an existing agent, extract the answers from the `.agent` file and project files.
61
+
62
+ **Resolve as many questions as possible from available context before asking the human.** Scan existing code, project metadata, prior conversation, and any provided requirements. Only surface questions the human must answer — never forward this list verbatim.
63
+
64
+ ### Agent Identity & Purpose *(feeds Purpose & Scope)*
65
+
66
+ - What is the agent's name? (no spaces, letters/numbers/underscores only)
67
+ - What is the agent's primary purpose in one sentence?
68
+ - What should the welcome message say?
69
+ - What personality should the agent have? (professional, friendly, formal, casual)
70
+ - What error message should the agent show if something breaks?
71
+
72
+ ### Topics & Conversation Flow *(feeds Topic Map)*
73
+
74
+ - What distinct conversation areas (topics) does the agent need?
75
+ - Which topic is the entry point? (where conversations start)
76
+ - What are the possible transitions between topics?
77
+ - Are there topics that delegate to others and need to return?
78
+ - Are there guardrail topics (off-topic redirection, ambiguity handling, security gates)?
79
+
80
+ ### Reasoning & Instructions *(feeds Behavioral Intent)*
81
+
82
+ - What should the agent do in each topic?
83
+ - What conditions change the instructions? (if guest is premium, if step 1 is complete)
84
+ - Should the agent do anything before or after reasoning in a given topic? (e.g., security checks, data fetches, automatic transitions)
85
+ - What data transformations (if any) does the LLM need to do?
86
+
87
+ ### Actions & External Systems *(feeds Actions & Backing Logic)*
88
+
89
+ - What external systems does the agent call?
90
+ - Salesforce Flows (autolaunched only)
91
+ - Apex classes (invocable only)
92
+ - Prompt Templates
93
+ - External APIs (not directly; must be wrapped in Apex or Flow)
94
+ - For each action: What inputs? What outputs? When should it be available?
95
+ - What custom objects exist in the project? Scan `objects/` for `.object-meta.xml` files. Check relationships (lookup, master-detail) between objects — related objects often contain data the agent should expose even when not explicitly mentioned in the prompt.
96
+
97
+ ### State Management *(feeds Variables, Gating Logic)*
98
+
99
+ - What information must persist across the conversation? (customer name, preferences, process state)
100
+ - What external context is needed? (session ID, user record, linked fields)
101
+ - What conditions should trigger different behavior in the same topic? (is_premium, role, completed_steps)
102
+
103
+ ---
104
+
105
+ ## 3. Environment Prerequisites
106
+
107
+ **⚠️ MANDATORY: Run these checks immediately after determining the agent type during discovery.** Do not proceed to topic architecture or code generation until the environment is validated.
108
+
109
+ ### `AgentforceEmployeeAgent`
110
+
111
+ 1. Confirm the config block does NOT include `default_agent_user`. If the generated boilerplate includes it, remove it along with any MessagingSession linked variables and escalation topic.
112
+
113
+ **⚠️ Setting `default_agent_user` on an employee agent causes publish and preview to fail with an unhelpful "unknown error."**
114
+
115
+ ### `AgentforceServiceAgent`
116
+
117
+ REQUIRES `default_agent_user`. Query the org to find an active Einstein Agent User:
118
+
119
+ ```bash
120
+ sf data query --json -q "SELECT Username FROM User WHERE Profile.UserLicense.Name = 'Einstein Agent' AND IsActive = true LIMIT 5"
121
+ ```
122
+
123
+ **If results are returned:** Ask which username to use. Record choice in the Agent Spec Configuration section. Verify permissions per [Agent User Setup & Permissions](agent-user-setup.md).
124
+
125
+ **If no results are returned:** STOP. Do NOT invent a username. Ask if you should create a new user, then read [Agent User Setup & Permissions](agent-user-setup.md) for user creation instructions.
126
+
127
+ **WRONG:** Fabricating a username when query returns nothing
128
+ ```
129
+ default_agent_user: "myagent@example.com" # made up, will fail at publish
130
+ ```
131
+
132
+ **RIGHT:** Stopping and asking to create a new user
133
+ ```
134
+ "No Einstein Agent User found in this org. Would you like me to create one for you?"
135
+ ```
136
+
137
+ ### Recording Prerequisites in the Agent Spec
138
+
139
+ Add a "Configuration" section to the Agent Spec:
140
+
141
+ - **Agent type**: `AgentforceServiceAgent` or `AgentforceEmployeeAgent`
142
+ - **Default agent user**: confirmed username (service agents), or "N/A — employee agent"
143
+ - **Permissions verified**: yes/no — see [Agent User Setup & Permissions](agent-user-setup.md)
144
+
145
+ ---
146
+
147
+ ## 4. Topic Architecture
148
+
149
+ Topics are states in a finite state machine. When designing a new agent, plan your topic structure before writing code. When comprehending an existing agent, identify which topic strategies and architecture pattern it uses.
150
+
151
+ ### Topic Strategies
152
+
153
+ Every topic in an agent serves one of three roles: domain, guardrail, or escalation.
154
+
155
+ **Domain Topics.** The core conversation areas where the agent does its work. Each domain topic handles a specific area (orders, billing, weather, events) with its own instructions, actions, and state. Most agents have 1-5 domain topics.
156
+
157
+ **Guardrail Topics.** Specialized topics that enforce agent boundaries. The standard Agentforce template includes two guardrail topics by default: `off_topic` (redirects users back to the agent's scope) and `ambiguous_question` (asks for clarification instead of guessing). Preserve these when modifying existing agents.
158
+
159
+ ```agentscript
160
+ topic off_topic:
161
+ description: "Handle off-topic requests"
162
+ reasoning:
163
+ instructions: ->
164
+ | You asked about something outside my scope.
165
+ I can only help with [list your capabilities].
166
+ What can I help you with today?
167
+
168
+ topic ambiguous_question:
169
+ description: "Ask for clarification"
170
+ reasoning:
171
+ instructions: ->
172
+ | I didn't quite understand your request.
173
+ Can you provide more details about what you need?
174
+ ```
175
+
176
+ **Escalation Topics.** Hand off to a human via `@utils.escalate`. This is a permanent exit — the user leaves the agent for a support channel (phone, email, chat with a human). Once triggered, the agent session ends. The escalation action does NOT return.
177
+
178
+ ```agentscript
179
+ topic escalation:
180
+ reasoning:
181
+ actions:
182
+ escalate: @utils.escalate
183
+ description: "Connect with a human agent"
184
+ ```
185
+
186
+ ### Single-Topic vs. Multi-Topic
187
+
188
+ Decide this before choosing an architecture pattern.
189
+
190
+ Use **single-topic** if:
191
+ - The agent handles one domain only (FAQ, weather checker, status lookup)
192
+ - All interactions naturally stay in the same context
193
+ - No complex state transitions needed
194
+
195
+ Use **multi-topic** if:
196
+ - The agent handles multiple distinct domains (customer service: orders + billing + account)
197
+ - Different topics have different instructions or action sets
198
+ - Users may need to switch contexts mid-conversation
199
+ - You need different entry points or security gates
200
+
201
+ ### Architecture Patterns
202
+
203
+ **Hub-and-Spoke.** One central topic (the router) transitions to specialized domain topics. The router is typically the `start_agent` topic. Each spoke handles a specific domain and may transition back to the router or to other spokes. Use when the agent handles multiple distinct domains that don't naturally flow together.
204
+
205
+ Example: The Local Info Agent. The `topic_selector` topic (hub) routes to domain and guardrail topics (spokes).
206
+
207
+ ```agentscript
208
+ start_agent topic_selector:
209
+ reasoning:
210
+ actions:
211
+ go_to_weather: @utils.transition to @topic.local_weather
212
+ go_to_events: @utils.transition to @topic.local_events
213
+ go_to_hours: @utils.transition to @topic.resort_hours
214
+ go_to_off_topic: @utils.transition to @topic.off_topic
215
+ go_to_ambiguous_question: @utils.transition to @topic.ambiguous_question
216
+
217
+ # Domain topics — each has its own instructions and actions
218
+ topic local_weather:
219
+ reasoning:
220
+ instructions: | Handle weather questions.
221
+
222
+ topic local_events:
223
+ reasoning:
224
+ instructions: | Handle event questions.
225
+
226
+ # resort_hours, off_topic, ambiguous_question defined further down the file
227
+ ```
228
+
229
+ **Linear Flow.** Topics form a pipeline: start → step 1 → step 2 → step 3 → end. Users progress through stages without backtracking. Use for multi-step workflows with mandatory ordering (application forms, onboarding, troubleshooting trees).
230
+
231
+ ```agentscript
232
+ start_agent intake:
233
+ reasoning:
234
+ actions:
235
+ go_next: @utils.transition to @topic.verification
236
+
237
+ topic verification:
238
+ reasoning:
239
+ actions:
240
+ go_next: @utils.transition to @topic.details_gathering
241
+
242
+ topic details_gathering:
243
+ reasoning:
244
+ actions:
245
+ go_next: @utils.transition to @topic.confirmation
246
+ ```
247
+
248
+ **Escalation Chain.** Tiered support where each level has increasing capabilities. First-level resolves common issues with basic actions; second-level has access to more powerful actions or broader authority; final level escalates to a human. Use when support difficulty varies and you want to resolve simple issues quickly without involving higher tiers.
249
+
250
+ ```agentscript
251
+ topic level_1_support:
252
+ reasoning:
253
+ instructions: | Try to resolve the issue using the FAQ and basic troubleshooting.
254
+ actions:
255
+ check_faq: @actions.search_faq
256
+ escalate: @utils.transition to @topic.level_2_support
257
+
258
+ topic level_2_support:
259
+ reasoning:
260
+ instructions: | You have access to account tools. Try to resolve before escalating.
261
+ actions:
262
+ lookup_account: @actions.get_account_details
263
+ modify_account: @actions.update_account
264
+ escalate_to_human: @utils.escalate
265
+ ```
266
+
267
+ **Verification Gate.** A security or permission check before allowing access to protected topics. The gate validates the user, then transitions to the protected topic or denies access.
268
+
269
+ ```agentscript
270
+ start_agent security_gate:
271
+ reasoning:
272
+ actions:
273
+ go_admin: @utils.transition to @topic.admin_panel
274
+ available when @variables.user_role == "admin"
275
+ go_denied: @utils.transition to @topic.access_denied
276
+ available when @variables.user_role != "admin"
277
+
278
+ topic access_denied:
279
+ reasoning:
280
+ instructions: | You don't have permission to access this.
281
+ ```
282
+
283
+ **Single-Topic.** The entire agent is one topic — no transitions. Use for focused QA agents where all interactions stay in the same domain.
284
+
285
+ ```agentscript
286
+ start_agent faq:
287
+ description: "Answer questions about pricing"
288
+ reasoning:
289
+ instructions: | Answer questions about our pricing plans.
290
+ actions:
291
+ lookup_plan: @actions.get_plan_details
292
+ ```
293
+
294
+ ### Composing Patterns
295
+
296
+ Real agents often combine patterns. A hub-and-spoke agent may use a verification gate before protected spokes. A linear flow may include escalation exits at each stage. When composing, each topic still serves exactly one role (domain, guardrail, or escalation) — the architecture pattern determines how they connect.
297
+
298
+ ---
299
+
300
+ ## 5. Mapping Logic to Actions
301
+
302
+ Every action in Agent Script needs backing logic — a Salesforce implementation that does the work. When creating a new agent, identify existing backing logic and stub what's missing. When comprehending an existing agent, trace each action to its backing implementation to understand what it does.
303
+
304
+ ### Valid Backing Logic Types
305
+
306
+ The most common backing logic types are Apex, Flows, and Prompt Templates.
307
+
308
+ **Apex**: Only **invocable Apex classes** work. A regular Apex class, even if it has public methods, will not work. Invocable classes use two key annotations:
309
+
310
+ `@InvocableMethod` marks the entry point. Its attributes: `label` (human-readable name), `description` (what the method does). Read these when comprehending existing backing logic.
311
+
312
+ > ⚠️ **An Apex class can only have ONE `@InvocableMethod`.** If you need multiple actions, create separate classes — one per action.
313
+
314
+ `@InvocableVariable` marks each input and output field on the inner Request/Result classes. Its attributes: `label` (human-readable field name), `description` (what the field represents), `required` (whether the field must be provided). Use these to build action input/output definitions.
315
+
316
+ ```apex
317
+ // WRONG — regular class, not invocable
318
+ public class WeatherFetcher {
319
+ public static String getWeather(String date) { ... }
320
+ }
321
+
322
+ // RIGHT — invocable class with annotated I/O (multiline annotations)
323
+ public class WeatherFetcher {
324
+ public class Request {
325
+ @InvocableVariable(
326
+ label='Date'
327
+ description='Date to check weather for'
328
+ required=true
329
+ )
330
+ public Date dateToCheck;
331
+ }
332
+ public class Result {
333
+ @InvocableVariable(
334
+ label='Max Temp'
335
+ description='Maximum temperature in Fahrenheit'
336
+ )
337
+ public Decimal maxTemp;
338
+ @InvocableVariable(
339
+ label='Min Temp'
340
+ description='Minimum temperature in Fahrenheit'
341
+ )
342
+ public Decimal minTemp;
343
+ }
344
+ @InvocableMethod(
345
+ label='Fetch Weather'
346
+ description='Gets weather forecast for a given date'
347
+ )
348
+ public static List<Result> getWeather(List<Request> requests) { ... }
349
+ }
350
+ ```
351
+
352
+ Wire with: `target: "apex://ClassName"`
353
+
354
+ **Flows**: Only **autolaunched Flows** work. Screen Flows, record-triggered Flows, and schedule-triggered Flows will not work. The Flow must start only when explicitly invoked.
355
+
356
+ Wire with: `target: "flow://FlowApiName"`
357
+
358
+ **Prompt Templates**: Salesforce Prompt Templates (custom or industry-specific).
359
+
360
+ Wire with: `target: "prompt://TemplateName"` (short form). The long form `generatePromptResponse://TemplateName` also works but prefer the short form.
361
+
362
+ ### How to Identify Existing Backing Logic
363
+
364
+ Read `sfdx-project.json` and look at the `packageDirectories` array — each entry's `path` field tells you where source files live (typically `force-app/main/default/`).
365
+
366
+ Then scan for each type within those directories:
367
+
368
+ **Finding invocable Apex:** Search `classes/` for files containing `@InvocableMethod`. For each match, read the class to extract the `@InvocableVariable` annotations on its inner `Request` and `Result` classes — these define the action's input and output contract. Pay attention to the `@InvocableVariable` types: they map to Agent Script types (`String` → `string`, `Boolean` → `boolean`, `Decimal` → `number`, `Integer` → `integer`, `Date` → `date`, `Datetime` → `datetime`). See the full type mapping table in "Connecting Backing Logic to Action Definitions" below.
369
+
370
+ **Finding autolaunched Flows:** Search `flows/` for `.flow-meta.xml` files. Read each file and check the `<processType>` element. Only `AutoLaunchedFlow` is valid for actions. Examine the `<variables>` elements to identify inputs (`isInput=true`) and outputs (`isOutput=true`) with their data types.
371
+
372
+ **Finding Prompt Templates:** Search `promptTemplates/` for template metadata files. Review the template's input variables and output format.
373
+
374
+ ### How to Map Existing Implementations
375
+
376
+ For each candidate implementation, verify it matches what the action needs:
377
+
378
+ - **Input contract** — does the implementation accept the parameters the action will send?
379
+ - **Output contract** — does the implementation return data the agent needs?
380
+ - **Target format** — use the correct protocol (`apex://`, `flow://`, `prompt://`)
381
+
382
+ Example — existing Apex class `OrderLookup`:
383
+
384
+ ```apex
385
+ public class OrderLookup {
386
+ public class Request {
387
+ @InvocableVariable(required=true)
388
+ public String orderId;
389
+ }
390
+ public class Result {
391
+ @InvocableVariable public String status;
392
+ @InvocableVariable public Decimal amount;
393
+ @InvocableVariable public Date orderDate;
394
+ }
395
+ @InvocableMethod(label='Fetch Order')
396
+ public static List<Result> getOrderStatus(List<Request> requests) { ... }
397
+ }
398
+ ```
399
+
400
+ In the Agent Spec, record:
401
+ ```
402
+ check_order action:
403
+ Backing: Apex class OrderLookup (invocable)
404
+ Target: apex://OrderLookup
405
+ Inputs: orderId (string, required)
406
+ Outputs: status (string), amount (number), orderDate (date)
407
+ Status: IMPLEMENTED
408
+ ```
409
+
410
+ ### Connecting Backing Logic to Action Definitions
411
+
412
+ Each `@InvocableVariable` on the request class becomes an action input; each on the result class becomes an output. The `target` field points to the implementation.
413
+
414
+ **Critical: Input and output names must exactly match the Apex `@InvocableVariable` field names, character-for-character.** If the Apex field is `dateToCheck`, the Agent Script input must be `dateToCheck` — not `date_to_check`, not `DateToCheck`. The platform validates these names at publish time; mismatches cause publish failures.
415
+
416
+ ```agentscript
417
+ # WRONG — snake_case doesn't match the Apex field names
418
+ topic orders:
419
+ actions:
420
+ check_order: @actions.check_order
421
+ target: "apex://OrderLookup"
422
+ inputs:
423
+ order_id: string # Apex field is orderId, NOT order_id
424
+ outputs:
425
+ order_date: date # Apex field is orderDate, NOT order_date
426
+
427
+ # RIGHT — names match Apex @InvocableVariable field names exactly
428
+ topic orders:
429
+ actions:
430
+ check_order: @actions.check_order
431
+ target: "apex://OrderLookup"
432
+ description: "Look up order status"
433
+ inputs:
434
+ orderId: string # matches Request.orderId
435
+ outputs:
436
+ status: string # matches Result.status
437
+ filter_from_agent: False
438
+ amount: number # matches Result.amount (Decimal → number)
439
+ filter_from_agent: False
440
+ orderDate: date # matches Result.orderDate (Date → date)
441
+ filter_from_agent: False
442
+ ```
443
+
444
+ #### Primitive Agent Script Type Mapping
445
+
446
+ Primitive types (individual and arrays) require only an Agent Script type.
447
+
448
+ | Agent Script Type | Apex | Flow | Prompt Template |
449
+ |---|---|---|---|
450
+ | `string` | String | Text | UNGROUNDED |
451
+ | `boolean` | Boolean | Boolean | UNGROUNDED |
452
+ | `number` | Decimal | UNGROUNDED | UNGROUNDED |
453
+ | `integer` | Integer | UNGROUNDED | UNGROUNDED |
454
+ | `long` | Long | UNGROUNDED | UNGROUNDED |
455
+ | `date` | Date | Date | UNGROUNDED |
456
+ | `datetime` | Datetime | UNGROUNDED | UNGROUNDED |
457
+ | `list[T]` | `List<T>` | UNGROUNDED | UNGROUNDED |
458
+
459
+ `integer`, `long`, and `datetime` are valid in action I/O only — not valid for agent variables.
460
+
461
+ #### Complex Agent Script Type Mapping
462
+
463
+ Complex types (Apex classes, SObject records) require both `object` or `list[object]` AND `complex_data_type_name`. **Correct value depends on action `target`, not data shape.**
464
+
465
+ | Target | Backing Logic Type | Agent Script Type | `complex_data_type_name` Format | Example |
466
+ |---|---|---|---|---|
467
+ | `apex://` | `List<InnerClass>` | `list[object]` | `@apexClassType/c__Class$InnerClass` | `@apexClassType/c__StationSupplyChecker$SupplyInfo` |
468
+ | `apex://` | `InnerClass` (single) | `object` | `@apexClassType/c__Class$InnerClass` | `@apexClassType/c__StationSupplyChecker$SupplyInfo` |
469
+ | `flow://` | SObject collection | `list[object]` | `lightning__recordInfoType` | `lightning__recordInfoType` |
470
+ | `flow://` | Single SObject | `object` | `lightning__recordInfoType` | `lightning__recordInfoType` |
471
+ | `prompt://` | UNGROUNDED | UNGROUNDED | UNGROUNDED | UNGROUNDED |
472
+
473
+ Format: `@apexClassType/c__<OuterClass>$<InnerClass>`. The `c__` prefix is the default namespace. The `$` separates outer from inner class.
474
+
475
+ NEVER use `lightning__recordInfoType` for `apex://` targets. ONLY use for Flow SObject returns.
476
+
477
+ ```agentscript
478
+ # WRONG — lightning__recordInfoType with apex:// target
479
+ get_properties:
480
+ target: "apex://PropertyQueryService"
481
+ outputs:
482
+ properties: list[object]
483
+ complex_data_type_name: "lightning__recordInfoType"
484
+
485
+ # WRONG — outer class name only, missing $InnerClass
486
+ get_properties:
487
+ target: "apex://PropertyQueryService"
488
+ outputs:
489
+ properties: list[object]
490
+ complex_data_type_name: "@apexClassType/c__PropertyQueryService"
491
+
492
+ # RIGHT — full inner class path, matches apex:// target
493
+ get_properties:
494
+ target: "apex://PropertyQueryService"
495
+ outputs:
496
+ properties: list[object]
497
+ complex_data_type_name: "@apexClassType/c__PropertyQueryService$PropertyInfo"
498
+ ```
499
+
500
+ Example — `flow://` target returning records:
501
+ ```agentscript
502
+ get_customer:
503
+ target: "flow://GetCustomerInfo"
504
+ outputs:
505
+ customer_info: object
506
+ complex_data_type_name: "lightning__recordInfoType"
507
+ ```
508
+
509
+ Example — `apex://` target returning structured data:
510
+ ```agentscript
511
+ check_supplies:
512
+ target: "apex://StationSupplyChecker"
513
+ outputs:
514
+ supplies: list[object]
515
+ complex_data_type_name: "@apexClassType/c__StationSupplyChecker$SupplyInfo"
516
+ ```
517
+
518
+ #### Output Visibility (`filter_from_agent`)
519
+
520
+ Each output requires a visibility decision: Should the agent display this value to the user, or keep it internal for routing and logic?
521
+
522
+ The `filter_from_agent` property controls this. The name is inverted — `True` means the output is **filtered out** (hidden from the user), `False` means it is **visible**.
523
+
524
+ Capture this decision during spec creation using the **Visible to User?** column in the Agent Spec template. Wrong choice causes agent to retrieve data but never display it.
525
+
526
+ | `filter_from_agent` | User sees the value? |
527
+ |---|---|
528
+ | `False` | Yes — displayed in the agent's response |
529
+ | `True` | No — available to the LLM for reasoning but not shown |
530
+
531
+ **Show** outputs the user asked for: records, summaries, computed results, status messages.
532
+
533
+ **Hide** outputs that are internal plumbing: success flags (`isSuccess`, `hasData`), IDs consumed by downstream actions, routing signals used in `available when` gates.
534
+
535
+ ```agentscript
536
+ get_properties:
537
+ target: "apex://PropertyQueryService"
538
+ outputs:
539
+ properties: list[object]
540
+ filter_from_agent: False # Desired info. Show to user
541
+ complex_data_type_name: "@apexClassType/c__PropertyQueryService$PropertyInfo"
542
+ hasData: boolean
543
+ filter_from_agent: True # Internal flag. Hide from user
544
+ ```
545
+
546
+ **⚠️ Invalid backing logic (non-autolaunched Flow, non-invocable Apex) may pass validation and simulation-mode preview. The failure surfaces at deploy or as cryptic runtime errors in live mode.** Always verify the backing logic type before wiring.
547
+
548
+ ### How to Stub Missing Logic
549
+
550
+ When no backing logic exists for an action, stub it as an invocable Apex class. Always use Apex for stubs — do not attempt to hand-craft Flow XML or Prompt Template metadata.
551
+
552
+ First, record the stub in the Agent Spec:
553
+ ```
554
+ fetch_invoice action:
555
+ Backing: (needs creation)
556
+ Target: apex://InvoiceFetcher (proposed)
557
+ Inputs: invoiceId (string, required)
558
+ Outputs: invoiceAmount (number), dueDate (date), status (string)
559
+ Requirements: Invocable Apex class that accepts invoiceId,
560
+ queries Invoice records, returns amount/dueDate/status
561
+ ```
562
+
563
+ Second, find the default package directory by reading `sfdx-project.json` at the project root and locating the `packageDirectories` entry where `"default": true`. The `path` value in that entry is the package root (commonly `force-app`, but not guaranteed).
564
+
565
+ Third, generate an empty Apex class using the following command:
566
+
567
+ ```bash
568
+ sf template generate apex class --name InvoiceFetcher --output-dir <PACKAGE_DIR>/main/default/classes
569
+ ```
570
+
571
+ This creates both the `.cls` and `.cls-meta.xml` files. Do not create test classes for stubs.
572
+
573
+ **Stub vs. functional backing logic.** If the prompt implies data access ("grounded in X data," "query Y records," "look up Z"), write functional Apex with bulkified SOQL per `assets/invocable-apex-template.cls`. Prefer static SOQL. If dynamic SOQL is required, NEVER append `WITH USER_MODE` to the query string — use `Database.query(q, AccessLevel.USER_MODE)` instead. See *Dynamic SOQL* in the template.
574
+
575
+ If the prompt does not imply data access, or if the action's data requirements are unclear, write a minimal stub — hardcoded return values only. Do not add SOQL, conditional logic, or complex inner class structures to minimal stubs.
576
+
577
+ Fourth, replace the generated class body with a stub. Use multiline `@InvocableVariable` annotations per `assets/invocable-apex-template.cls`:
578
+
579
+ ```apex
580
+ public class InvoiceFetcher {
581
+ public class Request {
582
+ @InvocableVariable(
583
+ label='Invoice ID'
584
+ description='ID of the invoice to fetch'
585
+ required=true
586
+ )
587
+ public String invoiceId;
588
+ }
589
+ public class Result {
590
+ @InvocableVariable(
591
+ label='Invoice Amount'
592
+ description='Total amount of the invoice'
593
+ )
594
+ public Decimal invoiceAmount;
595
+ @InvocableVariable(
596
+ label='Due Date'
597
+ description='Payment due date'
598
+ )
599
+ public Date dueDate;
600
+ @InvocableVariable(
601
+ label='Status'
602
+ description='Current invoice status'
603
+ )
604
+ public String status;
605
+ }
606
+ @InvocableMethod(
607
+ label='Fetch Invoice'
608
+ description='Retrieves invoice details by ID'
609
+ )
610
+ public static List<Result> fetch(List<Request> requests) {
611
+ // Stub — return minimal hardcoded values to unblock deployment
612
+ Result r = new Result();
613
+ r.status = 'stub';
614
+ return new List<Result>{ r };
615
+ }
616
+ }
617
+ ```
618
+
619
+ ALWAYS deploy one class at a time to isolate compile errors:
620
+
621
+ `sf project deploy start --json --metadata ApexClass:<ClassName>`
622
+
623
+ ALWAYS fix deploy errors BEFORE generating and deploying the next stub.
624
+
625
+ ---
626
+
627
+ ## 6. Transition Patterns
628
+
629
+ When creating a new agent, label every transition in your Agent Spec's Topic Map as either **handoff** or **delegation**. When analyzing an existing agent, classify each transition to determine whether context flow matches the design intent.
630
+
631
+ ### Handoff: Permanent Transition
632
+
633
+ A handoff is a one-way transition. The user moves to a new topic and control never returns to the original topic. Handoffs use `@utils.transition to` in `reasoning.actions`.
634
+
635
+ Use handoff when:
636
+ - Switching modes (preview → confirm → complete)
637
+ - Entry point routing (topic_selector → domain topics)
638
+ - One-way workflows (checkout → order_confirmation → end)
639
+
640
+ ```agentscript
641
+ topic topic_selector:
642
+ reasoning:
643
+ actions:
644
+ go_to_checkout: @utils.transition to @topic.checkout
645
+ description: "Start checkout"
646
+
647
+ topic checkout:
648
+ reasoning:
649
+ actions:
650
+ go_to_confirm: @utils.transition to @topic.order_confirmation
651
+ description: "Proceed to confirmation"
652
+ ```
653
+
654
+ After `go_to_confirm` executes, the user is in `order_confirmation`. If they later say "go back," the agent routes them back through `topic_selector` (the entry point), not to `checkout`. Handoffs don't stack; they reset the conversation state.
655
+
656
+ ### Delegation: Handoff with Explicit Return
657
+
658
+ Delegation hands control to another topic using `@topic.X` in `reasoning.actions`. It signals *intent* to return, but the return does not happen automatically — the delegated topic must explicitly transition back to the caller.
659
+
660
+ Use delegation when:
661
+ - One topic needs advice from a specialist and should continue after
662
+ - Reusable sub-workflows (e.g., identity verification called from multiple topics)
663
+ - A topic needs to temporarily visit another topic, then resume
664
+
665
+ **Critical Rule:** `@topic.X` delegates control. It does NOT implement call-return semantics. If you want the user to return to the calling topic, code an explicit `transition to @topic.<caller>` in the delegated topic. Without it, the next user utterance falls through to `topic_selector`.
666
+
667
+ WRONG: Assuming `@topic.specialist` returns automatically
668
+ ```agentscript
669
+ topic main:
670
+ reasoning:
671
+ actions:
672
+ consult_specialist: @topic.specialist # WRONG — assumes return
673
+
674
+ # After specialist runs, control does NOT return to main.
675
+ # The next user utterance routes through topic_selector.
676
+ ```
677
+
678
+ RIGHT: Delegated topic defines explicit return transition
679
+ ```agentscript
680
+ topic main:
681
+ reasoning:
682
+ actions:
683
+ consult_specialist: @topic.specialist
684
+ description: "Consult specialist"
685
+
686
+ topic specialist:
687
+ reasoning:
688
+ actions:
689
+ go_to_main: @utils.transition to @topic.main
690
+ description: "Return to main"
691
+ ```
692
+
693
+ ---
694
+
695
+ ## 7. Deterministic vs. Subjective Flow Control
696
+
697
+ Instructions are suggestions the LLM *may* follow. Gates and guards are enforced by the runtime and *cannot* be bypassed. For every requirement, choose the right flow control type.
698
+
699
+ ### Classifying Flow Control Requirements
700
+
701
+ **Deterministic flow control** — the runtime enforces it. Use when the requirement is non-negotiable:
702
+ - Security: "only admin users can access this"
703
+ - Financial: "never approve transactions above $10,000 without human review"
704
+ - State: "don't show the payment form until the user provides a delivery address"
705
+ - Counter: "you can only call this action once per session"
706
+
707
+ **Subjective flow control** — the LLM decides. Use when flexibility is acceptable:
708
+ - Conversational tone: "respond professionally but warmly"
709
+ - Natural language generation: "summarize the results in your own words"
710
+ - User preferences: "if the user is impatient, give short answers; if curious, explain more"
711
+
712
+ **The test:** what happens if the LLM gets this wrong? If the answer is a security breach, financial error, or broken workflow → deterministic. If the answer is an awkward response or suboptimal tone → subjective.
713
+
714
+ WRONG: Security rule as an instruction (LLM can ignore it)
715
+ ```agentscript
716
+ topic admin_panel:
717
+ reasoning:
718
+ instructions: ->
719
+ | Only respond if the user is an admin.
720
+ If they are not an admin, tell them access is denied.
721
+ ```
722
+
723
+ The LLM may comply, or it may not — instructions are suggestions. The RIGHT approach uses a `before_reasoning` guard that the runtime enforces before the LLM is ever invoked. See Section 8 for all gating mechanisms.
724
+
725
+ ### Writing Effective Instructions
726
+
727
+ Two factors govern subjective control effectiveness: instruction ordering and grounding.
728
+
729
+ **Instruction Ordering.** The runtime resolves instructions top-to-bottom — evaluating `if/else` blocks and expanding template expressions — before the LLM sees the result. The resolved text becomes the LLM's prompt. Put post-action checks first, data references next, dynamic conditional text last.
730
+
731
+ RIGHT: Post-action check at the top (LLM sees it first)
732
+ ```agentscript
733
+ topic checkout:
734
+ reasoning:
735
+ instructions: ->
736
+ # Post-action check — LLM sees this first
737
+ if @variables.cart_validation_failed:
738
+ | Your cart has items that are no longer available.
739
+ Please remove them and try again.
740
+
741
+ # Data reference — LLM sees the resolved value
742
+ | Your current cart total is {!@variables.cart_total}.
743
+
744
+ # Dynamic instructions — conditional on state
745
+ if @variables.is_premium:
746
+ | You qualify for FREE shipping.
747
+ else:
748
+ | Standard shipping is {!@variables.shipping_cost}.
749
+
750
+ | Proceed to payment or cancel?
751
+ ```
752
+
753
+ WRONG: Post-action check at the bottom (LLM may respond before seeing it)
754
+ ```agentscript
755
+ topic checkout:
756
+ reasoning:
757
+ instructions: ->
758
+ | Your current cart total is {!@variables.cart_total}.
759
+ Proceed to payment or cancel?
760
+
761
+ # Too late — LLM may already be generating a response
762
+ if @variables.cart_validation_failed:
763
+ | Your cart has items that are no longer available.
764
+ ```
765
+
766
+ **Grounding.** The platform's grounding service validates that the agent's response matches action output data. Paraphrasing or embellishing may cause grounding failures.
767
+
768
+ - Use specific values: `"The event is on {!@variables.event_date}"` grounds reliably; `"The event is next week"` may not.
769
+ - Avoid transforming values: return `"Tuesday"` as-is, not `"day after Monday"`.
770
+ - Avoid embellishment instructions: `"Respond like a pirate"` increases grounding risk — embellished content has no output to ground against.
771
+
772
+ Grounding validation requires **live mode preview** (`sf agent preview --use-live-actions --json`). Simulated mode preview generates fake outputs, so grounding has nothing real to validate against.
773
+
774
+ **Naming output fields in post-action instructions.** ALWAYS specify which output fields to include in text responses. Generic instructions like "present the results clearly" let platform-injected tools hijack the response. EXAMPLE: The LLM calls `show_command` instead of composing text, producing generic "Here are the results:" message wrapper with raw structured data. This can corrupt session state, causing subsequent turns to fail with generic "something went wrong" message. Naming output fields steers the LLM toward composing a direct text response. This reliably grounds the response because it maps closely to action output values. ALWAYS include `Do NOT use the show_command tool. Always compose your response as direct text.` in post-action instructions. See *Anti-Patterns* in the *Core Language* reference for full WRONG/RIGHT example.
775
+
776
+ ### Post-Action Behavior
777
+
778
+ When an action completes without triggering a transition, the topic stays active. The runtime re-evaluates the entire topic — resolving instructions top-to-bottom again with updated variables, then passing the new prompt to the LLM. The LLM may call the same action again. To prevent unwanted loops, see Section 9 (Action Loop Prevention).
779
+
780
+ ---
781
+
782
+ ## 8. Gating Patterns
783
+
784
+ ### `available when` — Action Visibility Gate
785
+
786
+ An action marked `available when <condition>` is hidden from the LLM when the condition is false. The LLM cannot call an unavailable action.
787
+
788
+ **WRONG: Relying on instructions to prevent action calls**
789
+ ```agentscript
790
+ topic booking:
791
+ reasoning:
792
+ instructions: ->
793
+ | if @variables.booking_pending:
794
+ Do NOT call {!@actions.confirm_booking} yet.
795
+
796
+ actions:
797
+ confirm: @actions.confirm_booking # Always visible
798
+ ```
799
+
800
+ The action is visible; instructions tell the LLM not to call it. The LLM may ignore instructions.
801
+
802
+ **RIGHT: Using `available when` to hide the action**
803
+ ```agentscript
804
+ topic booking:
805
+ reasoning:
806
+ actions:
807
+ confirm: @actions.confirm_booking
808
+ available when @variables.booking_pending == True
809
+ ```
810
+
811
+ If `booking_pending` is False, the LLM sees no `confirm` action.
812
+
813
+ ### Conditional Instructions — Prompt Text Gate
814
+
815
+ Use `if/else` in instructions to show/hide text based on state. This doesn't hide actions; it changes what the LLM is told to do.
816
+
817
+ ```agentscript
818
+ topic support:
819
+ reasoning:
820
+ instructions: ->
821
+ | You're helping a customer with their order.
822
+
823
+ if @variables.is_vip:
824
+ | This is a VIP customer. Prioritize their request
825
+ and offer alternatives if the first option isn't available.
826
+ else:
827
+ | Follow standard support procedures.
828
+
829
+ | What can I help you with?
830
+ ```
831
+
832
+ Use conditional instructions when you want to steer the LLM's reasoning without hiding actions entirely.
833
+
834
+ ### `before_reasoning` Guards — Early Exit
835
+
836
+ The `before_reasoning` block runs before the LLM is invoked. Code here executes every time the topic is entered. The LLM never sees it, cannot override it, and cannot skip it.
837
+
838
+ ```agentscript
839
+ topic admin_panel:
840
+ before_reasoning:
841
+ if @variables.user_role != "admin":
842
+ transition to @topic.access_denied
843
+
844
+ reasoning:
845
+ instructions: | You are in the admin panel.
846
+ ```
847
+
848
+ If the user is not an admin, they transition out before the LLM is invoked. The admin topic's reasoning instructions never execute.
849
+
850
+ ### Multi-Condition Gating
851
+
852
+ Combine `available when`, conditional instructions, and guards to enforce complex rules.
853
+
854
+ Example: "Show the payment action only if the user is authenticated AND the cart is not empty AND we're not in a preview/demo mode"
855
+
856
+ ```agentscript
857
+ topic checkout:
858
+ before_reasoning:
859
+ if @variables.is_demo_mode:
860
+ transition to @topic.demo_complete
861
+
862
+ reasoning:
863
+ instructions: ->
864
+ | Review your order.
865
+
866
+ if @variables.items_in_cart == 0:
867
+ | Your cart is empty. Go back and select items.
868
+
869
+ actions:
870
+ pay: @actions.process_payment
871
+ available when @variables.authenticated == True
872
+ and @variables.items_in_cart > 0
873
+ ```
874
+
875
+ ### Sequential Gate Pattern
876
+
877
+ Track progress through validation stages using state variables.
878
+
879
+ ```agentscript
880
+ variables:
881
+ step1_verified: mutable boolean = False
882
+ step2_verified: mutable boolean = False
883
+ step3_verified: mutable boolean = False
884
+
885
+ topic verification:
886
+ reasoning:
887
+ actions:
888
+ verify_step1: @actions.run_check_1
889
+ verify_step2: @actions.run_check_2
890
+ available when @variables.step1_verified == True
891
+ verify_step3: @actions.run_check_3
892
+ available when @variables.step2_verified == True
893
+ proceed: @utils.transition to @topic.confirmed
894
+ available when @variables.step3_verified == True
895
+ ```
896
+
897
+ Each step becomes visible only after the previous step completes (updates its variable). This gates the entire flow.
898
+
899
+ ### Same-Turn Behavior After Gate Transitions
900
+
901
+ When a gate topic (e.g., username collection) uses `after_reasoning` to transition into a routing topic, both topics process in the **same user turn**. The router receives the user's original message — the one that satisfied the gate — not a fresh utterance.
902
+
903
+ This means if the user said "My username is alex" and the gate transitions to a topic selector, the selector's reasoning fires against "My username is alex." Since that message doesn't match any domain topic, the router may misclassify it (e.g., routing to `off_topic`).
904
+
905
+ **Mitigation:** Write the router's reasoning instructions defensively. Tell the LLM that if the user just arrived from the gate, it should greet them and ask how it can help instead of routing the triggering message. See the Anti-Patterns section in the Core Language reference for a full WRONG/RIGHT example.
906
+
907
+ ---
908
+
909
+ ## 9. Action Loop Prevention
910
+
911
+ An action loop occurs when the LLM calls the same action repeatedly without new user input. Three things combine to cause loops:
912
+
913
+ - **No `available when` gate.** An action without an `available when` condition appears in the LLM's context every reasoning cycle. There is no mechanism that automatically hides an action after it executes — if you don't gate it, it stays visible indefinitely.
914
+ - **Variable-bound input.** When you bind an input to a variable (`with param = @variables.x`), the action is "ready to go" every cycle — the LLM doesn't need to extract values from the conversation. It can invoke the action with zero friction.
915
+ - **No post-action instructions.** The instructions don't tell the LLM what to do after the action completes, so it may call the action again.
916
+
917
+ **WRONG: All three loop conditions present**
918
+ ```agentscript
919
+ topic events:
920
+ reasoning:
921
+ instructions: ->
922
+ | Use the {!@actions.check_events} action to find events.
923
+
924
+ actions:
925
+ check_events: @actions.check_events
926
+ with interest = @variables.guest_interest # Variable-bound input
927
+ ```
928
+
929
+ No gate, variable-bound input, no post-action guidance. The LLM can call `check_events` every cycle.
930
+
931
+ ### Three Mitigations
932
+
933
+ **1. Explicit Post-Action Instructions (most common).**
934
+
935
+ Tell the LLM to stop calling the action after receiving results. Name the specific output fields the LLM should include in its text response — vague instructions like "present the results" let platform tools hijack the response (see Section 7, Grounding).
936
+
937
+ ```agentscript
938
+ topic events:
939
+ reasoning:
940
+ instructions: ->
941
+ | Use {!@actions.check_events} to find events matching the guest's interest.
942
+ After you receive the results, write the data directly in your text response.
943
+ For each event, include the eventName, eventDate, and location values from
944
+ the action output. Use the exact values returned — do NOT paraphrase or round.
945
+ Do NOT call the action again — you already have the information you need.
946
+ Do NOT use the show_command tool. Always compose your response as direct text.
947
+
948
+ actions:
949
+ check_events: @actions.check_events
950
+ with interest = @variables.guest_interest
951
+ ```
952
+
953
+ **2. Post-Action Transitions (state-based).**
954
+
955
+ Move the agent out of the topic after the action completes, breaking the cycle.
956
+
957
+ ```agentscript
958
+ topic events:
959
+ reasoning:
960
+ instructions: ->
961
+ | Use {!@actions.check_events} to find events.
962
+
963
+ actions:
964
+ check_events: @actions.check_events
965
+ with interest = @variables.guest_interest
966
+
967
+ after_reasoning:
968
+ if @outputs.events_found:
969
+ transition to @topic.results_displayed
970
+ ```
971
+
972
+ After `check_events` runs, the `after_reasoning` block transitions to a new topic. The agent never cycles back to `events`, so the action can't be called again.
973
+
974
+ **3. LLM Slot-Filling Over Variable Binding (friction-based).**
975
+
976
+ Use `...` (LLM slot-fill) instead of variable binding. This forces the LLM to extract values from the conversation each cycle, adding natural decision friction.
977
+
978
+ ```agentscript
979
+ topic search:
980
+ reasoning:
981
+ instructions: ->
982
+ | Help the user search for products.
983
+ Ask them what they're looking for, then use {!@actions.search} to find matches.
984
+
985
+ actions:
986
+ search: @actions.search
987
+ with query = ... # LLM must extract the query each time
988
+ ```
989
+
990
+ With `...`, the LLM must actively decide "do I have a new search query?" on every cycle.
991
+
992
+ **Combine mitigations for reinforcement:**
993
+
994
+ ```agentscript
995
+ topic lookup:
996
+ reasoning:
997
+ instructions: ->
998
+ | Once you have the result, present it. Do NOT call the action again.
999
+
1000
+ actions:
1001
+ lookup: @actions.find_data
1002
+ with key = ... # Requires extraction each time
1003
+
1004
+ after_reasoning:
1005
+ if @outputs.data_found:
1006
+ transition to @topic.done # Exit the topic
1007
+ ```
1008
+
1009
+ Combine mitigations for reinforcement.
1010
+