@falai/agent 0.6.8 → 0.7.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 (231) hide show
  1. package/README.md +62 -59
  2. package/dist/adapters/MemoryAdapter.js +2 -2
  3. package/dist/adapters/MemoryAdapter.js.map +1 -1
  4. package/dist/adapters/MongoAdapter.js +2 -2
  5. package/dist/adapters/MongoAdapter.js.map +1 -1
  6. package/dist/adapters/OpenSearchAdapter.js +7 -7
  7. package/dist/adapters/OpenSearchAdapter.js.map +1 -1
  8. package/dist/adapters/PostgreSQLAdapter.js +9 -9
  9. package/dist/adapters/PostgreSQLAdapter.js.map +1 -1
  10. package/dist/adapters/PrismaAdapter.js +3 -3
  11. package/dist/adapters/PrismaAdapter.js.map +1 -1
  12. package/dist/adapters/RedisAdapter.js +2 -2
  13. package/dist/adapters/RedisAdapter.js.map +1 -1
  14. package/dist/adapters/SQLiteAdapter.d.ts +3 -3
  15. package/dist/adapters/SQLiteAdapter.d.ts.map +1 -1
  16. package/dist/adapters/SQLiteAdapter.js +11 -11
  17. package/dist/adapters/SQLiteAdapter.js.map +1 -1
  18. package/dist/adapters/index.d.ts +1 -1
  19. package/dist/adapters/index.d.ts.map +1 -1
  20. package/dist/cjs/adapters/MemoryAdapter.js +2 -2
  21. package/dist/cjs/adapters/MemoryAdapter.js.map +1 -1
  22. package/dist/cjs/adapters/MongoAdapter.js +2 -2
  23. package/dist/cjs/adapters/MongoAdapter.js.map +1 -1
  24. package/dist/cjs/adapters/OpenSearchAdapter.js +7 -7
  25. package/dist/cjs/adapters/OpenSearchAdapter.js.map +1 -1
  26. package/dist/cjs/adapters/PostgreSQLAdapter.js +9 -9
  27. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -1
  28. package/dist/cjs/adapters/PrismaAdapter.js +3 -3
  29. package/dist/cjs/adapters/PrismaAdapter.js.map +1 -1
  30. package/dist/cjs/adapters/RedisAdapter.js +2 -2
  31. package/dist/cjs/adapters/RedisAdapter.js.map +1 -1
  32. package/dist/cjs/adapters/SQLiteAdapter.d.ts +3 -3
  33. package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +1 -1
  34. package/dist/cjs/adapters/SQLiteAdapter.js +11 -11
  35. package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -1
  36. package/dist/cjs/adapters/index.d.ts +1 -1
  37. package/dist/cjs/adapters/index.d.ts.map +1 -1
  38. package/dist/cjs/constants/index.d.ts +4 -4
  39. package/dist/cjs/constants/index.js +5 -5
  40. package/dist/cjs/core/Agent.d.ts +22 -22
  41. package/dist/cjs/core/Agent.d.ts.map +1 -1
  42. package/dist/cjs/core/Agent.js +160 -152
  43. package/dist/cjs/core/Agent.js.map +1 -1
  44. package/dist/cjs/core/Events.d.ts +6 -6
  45. package/dist/cjs/core/Events.d.ts.map +1 -1
  46. package/dist/cjs/core/PersistenceManager.d.ts +13 -13
  47. package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
  48. package/dist/cjs/core/PersistenceManager.js +24 -24
  49. package/dist/cjs/core/PersistenceManager.js.map +1 -1
  50. package/dist/cjs/core/ResponseEngine.d.ts +3 -8
  51. package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
  52. package/dist/cjs/core/ResponseEngine.js +8 -8
  53. package/dist/cjs/core/ResponseEngine.js.map +1 -1
  54. package/dist/cjs/core/Route.d.ts +17 -17
  55. package/dist/cjs/core/Route.d.ts.map +1 -1
  56. package/dist/cjs/core/Route.js +33 -33
  57. package/dist/cjs/core/Route.js.map +1 -1
  58. package/dist/cjs/core/RoutingEngine.d.ts +30 -30
  59. package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
  60. package/dist/cjs/core/RoutingEngine.js +192 -192
  61. package/dist/cjs/core/RoutingEngine.js.map +1 -1
  62. package/dist/cjs/core/Step.d.ts +72 -0
  63. package/dist/cjs/core/Step.d.ts.map +1 -0
  64. package/dist/cjs/core/Step.js +150 -0
  65. package/dist/cjs/core/Step.js.map +1 -0
  66. package/dist/cjs/core/ToolExecutor.d.ts +5 -5
  67. package/dist/cjs/core/ToolExecutor.d.ts.map +1 -1
  68. package/dist/cjs/core/ToolExecutor.js +8 -8
  69. package/dist/cjs/core/ToolExecutor.js.map +1 -1
  70. package/dist/cjs/core/Transition.d.ts +14 -14
  71. package/dist/cjs/core/Transition.d.ts.map +1 -1
  72. package/dist/cjs/core/Transition.js +48 -19
  73. package/dist/cjs/core/Transition.js.map +1 -1
  74. package/dist/cjs/index.d.ts +7 -7
  75. package/dist/cjs/index.d.ts.map +1 -1
  76. package/dist/cjs/index.js +8 -8
  77. package/dist/cjs/index.js.map +1 -1
  78. package/dist/cjs/types/agent.d.ts +8 -8
  79. package/dist/cjs/types/agent.d.ts.map +1 -1
  80. package/dist/cjs/types/ai.d.ts +2 -2
  81. package/dist/cjs/types/ai.d.ts.map +1 -1
  82. package/dist/cjs/types/history.d.ts +3 -3
  83. package/dist/cjs/types/history.d.ts.map +1 -1
  84. package/dist/cjs/types/index.d.ts +1 -1
  85. package/dist/cjs/types/index.d.ts.map +1 -1
  86. package/dist/cjs/types/persistence.d.ts +5 -5
  87. package/dist/cjs/types/persistence.d.ts.map +1 -1
  88. package/dist/cjs/types/route.d.ts +57 -52
  89. package/dist/cjs/types/route.d.ts.map +1 -1
  90. package/dist/cjs/types/session.d.ts +27 -27
  91. package/dist/cjs/types/session.d.ts.map +1 -1
  92. package/dist/cjs/types/session.js +48 -50
  93. package/dist/cjs/types/session.js.map +1 -1
  94. package/dist/cjs/types/tool.d.ts +13 -13
  95. package/dist/cjs/types/tool.d.ts.map +1 -1
  96. package/dist/cjs/utils/id.d.ts +8 -3
  97. package/dist/cjs/utils/id.d.ts.map +1 -1
  98. package/dist/cjs/utils/id.js +16 -7
  99. package/dist/cjs/utils/id.js.map +1 -1
  100. package/dist/constants/index.d.ts +4 -4
  101. package/dist/constants/index.js +4 -4
  102. package/dist/core/Agent.d.ts +22 -22
  103. package/dist/core/Agent.d.ts.map +1 -1
  104. package/dist/core/Agent.js +162 -154
  105. package/dist/core/Agent.js.map +1 -1
  106. package/dist/core/Events.d.ts +6 -6
  107. package/dist/core/Events.d.ts.map +1 -1
  108. package/dist/core/PersistenceManager.d.ts +13 -13
  109. package/dist/core/PersistenceManager.d.ts.map +1 -1
  110. package/dist/core/PersistenceManager.js +25 -25
  111. package/dist/core/PersistenceManager.js.map +1 -1
  112. package/dist/core/ResponseEngine.d.ts +3 -8
  113. package/dist/core/ResponseEngine.d.ts.map +1 -1
  114. package/dist/core/ResponseEngine.js +8 -8
  115. package/dist/core/ResponseEngine.js.map +1 -1
  116. package/dist/core/Route.d.ts +17 -17
  117. package/dist/core/Route.d.ts.map +1 -1
  118. package/dist/core/Route.js +33 -33
  119. package/dist/core/Route.js.map +1 -1
  120. package/dist/core/RoutingEngine.d.ts +30 -30
  121. package/dist/core/RoutingEngine.d.ts.map +1 -1
  122. package/dist/core/RoutingEngine.js +193 -193
  123. package/dist/core/RoutingEngine.js.map +1 -1
  124. package/dist/core/Step.d.ts +72 -0
  125. package/dist/core/Step.d.ts.map +1 -0
  126. package/dist/core/Step.js +146 -0
  127. package/dist/core/Step.js.map +1 -0
  128. package/dist/core/ToolExecutor.d.ts +5 -5
  129. package/dist/core/ToolExecutor.d.ts.map +1 -1
  130. package/dist/core/ToolExecutor.js +8 -8
  131. package/dist/core/ToolExecutor.js.map +1 -1
  132. package/dist/core/Transition.d.ts +14 -14
  133. package/dist/core/Transition.d.ts.map +1 -1
  134. package/dist/core/Transition.js +48 -19
  135. package/dist/core/Transition.js.map +1 -1
  136. package/dist/index.d.ts +7 -7
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +4 -4
  139. package/dist/index.js.map +1 -1
  140. package/dist/types/agent.d.ts +8 -8
  141. package/dist/types/agent.d.ts.map +1 -1
  142. package/dist/types/ai.d.ts +2 -2
  143. package/dist/types/ai.d.ts.map +1 -1
  144. package/dist/types/history.d.ts +3 -3
  145. package/dist/types/history.d.ts.map +1 -1
  146. package/dist/types/index.d.ts +1 -1
  147. package/dist/types/index.d.ts.map +1 -1
  148. package/dist/types/persistence.d.ts +5 -5
  149. package/dist/types/persistence.d.ts.map +1 -1
  150. package/dist/types/route.d.ts +57 -52
  151. package/dist/types/route.d.ts.map +1 -1
  152. package/dist/types/session.d.ts +27 -27
  153. package/dist/types/session.d.ts.map +1 -1
  154. package/dist/types/session.js +44 -46
  155. package/dist/types/session.js.map +1 -1
  156. package/dist/types/tool.d.ts +13 -13
  157. package/dist/types/tool.d.ts.map +1 -1
  158. package/dist/utils/id.d.ts +8 -3
  159. package/dist/utils/id.d.ts.map +1 -1
  160. package/dist/utils/id.js +14 -6
  161. package/dist/utils/id.js.map +1 -1
  162. package/docs/ADAPTERS.md +21 -21
  163. package/docs/AGENT.md +57 -55
  164. package/docs/API_REFERENCE.md +218 -220
  165. package/docs/ARCHITECTURE.md +99 -104
  166. package/docs/CONTEXT_MANAGEMENT.md +81 -88
  167. package/docs/DOCS.md +18 -18
  168. package/docs/DOMAINS.md +16 -16
  169. package/docs/EXAMPLES.md +43 -43
  170. package/docs/GETTING_STARTED.md +60 -63
  171. package/docs/PERSISTENCE.md +66 -70
  172. package/docs/PROVIDERS.md +2 -2
  173. package/docs/README.md +6 -6
  174. package/docs/ROUTES.md +218 -220
  175. package/docs/STEPS.md +883 -0
  176. package/examples/business-onboarding.ts +84 -81
  177. package/examples/company-qna-agent.ts +68 -67
  178. package/examples/custom-database-persistence.ts +87 -89
  179. package/examples/declarative-agent.ts +32 -32
  180. package/examples/domain-scoping.ts +18 -18
  181. package/examples/extracted-data-modification.ts +92 -97
  182. package/examples/healthcare-agent.ts +89 -91
  183. package/examples/openai-agent.ts +29 -32
  184. package/examples/opensearch-persistence.ts +43 -45
  185. package/examples/persistent-onboarding.ts +65 -66
  186. package/examples/prisma-persistence.ts +108 -112
  187. package/examples/prisma-schema.example.prisma +3 -3
  188. package/examples/redis-persistence.ts +67 -73
  189. package/examples/route-transitions.ts +71 -47
  190. package/examples/rules-prohibitions.ts +28 -28
  191. package/examples/streaming-agent.ts +24 -24
  192. package/examples/travel-agent.ts +94 -109
  193. package/package.json +1 -1
  194. package/src/adapters/MemoryAdapter.ts +3 -3
  195. package/src/adapters/MongoAdapter.ts +3 -3
  196. package/src/adapters/OpenSearchAdapter.ts +8 -8
  197. package/src/adapters/PostgreSQLAdapter.ts +10 -10
  198. package/src/adapters/PrismaAdapter.ts +4 -4
  199. package/src/adapters/RedisAdapter.ts +3 -3
  200. package/src/adapters/SQLiteAdapter.ts +15 -15
  201. package/src/adapters/index.ts +1 -1
  202. package/src/constants/index.ts +4 -4
  203. package/src/core/Agent.ts +210 -206
  204. package/src/core/Events.ts +12 -12
  205. package/src/core/PersistenceManager.ts +32 -36
  206. package/src/core/ResponseEngine.ts +11 -17
  207. package/src/core/Route.ts +55 -49
  208. package/src/core/RoutingEngine.ts +244 -252
  209. package/src/core/Step.ts +197 -0
  210. package/src/core/ToolExecutor.ts +11 -11
  211. package/src/core/Transition.ts +72 -26
  212. package/src/index.ts +8 -8
  213. package/src/types/agent.ts +8 -8
  214. package/src/types/ai.ts +2 -2
  215. package/src/types/history.ts +3 -3
  216. package/src/types/index.ts +1 -1
  217. package/src/types/persistence.ts +6 -6
  218. package/src/types/route.ts +77 -61
  219. package/src/types/session.ts +75 -78
  220. package/src/types/tool.ts +17 -17
  221. package/src/utils/id.ts +15 -6
  222. package/dist/cjs/core/State.d.ts +0 -72
  223. package/dist/cjs/core/State.d.ts.map +0 -1
  224. package/dist/cjs/core/State.js +0 -148
  225. package/dist/cjs/core/State.js.map +0 -1
  226. package/dist/core/State.d.ts +0 -72
  227. package/dist/core/State.d.ts.map +0 -1
  228. package/dist/core/State.js +0 -144
  229. package/dist/core/State.js.map +0 -1
  230. package/docs/STATES.md +0 -888
  231. package/src/core/State.ts +0 -212
package/docs/STEPS.md ADDED
@@ -0,0 +1,883 @@
1
+ # Steps Guide
2
+
3
+ A complete guide to creating and managing steps in conversational flows.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [What is a Step?](#what-is-a-step)
10
+ - [Creating Steps](#creating-steps)
11
+ - [Step Configuration](#step-configuration)
12
+ - [Transitions](#transitions)
13
+ - [Data Collecting](#data-collecting)
14
+ - [Step Logic](#step-logic)
15
+ - [Step Types](#step-types)
16
+ - [Advanced Patterns](#advanced-patterns)
17
+
18
+ ---
19
+
20
+ ## What is a Step?
21
+
22
+ A **Step** represents a specific step or moment in a conversation. Each step can:
23
+
24
+ - Display a message to the user (chat state)
25
+ - Execute a tool (tool state)
26
+ - Collect data from the conversation
27
+ - Make decisions about what to do next
28
+
29
+ ```typescript
30
+ // Example: A step that asks for user's name
31
+ const askName = route.initialStep.nextStep({
32
+ instructions: "What's your name?",
33
+ collect: ["firstName", "lastName"],
34
+ });
35
+ ```
36
+
37
+ **Key Concepts:**
38
+
39
+ - Steps form a **step machine** within a route
40
+ - Each step has a unique **ID** (auto-generated or custom)
41
+ - Steps can **collect data** from user responses
42
+ - Steps can be **skipped** based on conditions
43
+ - Steps can have **prerequisites** (required data)
44
+
45
+ ---
46
+
47
+ ## Creating Steps
48
+
49
+ ### Chat states
50
+
51
+ Chat states present a message and optionally collect data:
52
+
53
+ ```typescript
54
+ // Simple chat state
55
+ const welcome = route.initialStep.nextStep({
56
+ instructions: "Welcome! How can I help you today?",
57
+ });
58
+
59
+ // Chat state with data collecting
60
+ const askDestination = welcome.nextStep({
61
+ instructions: "Where would you like to fly?",
62
+ collect: ["destination"],
63
+ });
64
+
65
+ // Chat state with custom ID
66
+ const askDates = askDestination.nextStep({
67
+ id: "ask_travel_dates",
68
+ instructions: "When would you like to depart?",
69
+ collect: ["departureDate"],
70
+ });
71
+ ```
72
+
73
+ ### Tool states
74
+
75
+ Tool states execute functions and can update context or collected data:
76
+
77
+ ```typescript
78
+ import { defineTool } from "@falai/agent";
79
+
80
+ const searchFlights = defineTool<Context, [], Results>(
81
+ "search_flights",
82
+ async ({ context, data }) => {
83
+ const results = await api.search(data.destination);
84
+ return {
85
+ data: results,
86
+ contextUpdate: { availableFlights: results },
87
+ };
88
+ }
89
+ );
90
+
91
+ // Tool state
92
+ const searchStep = askDates.nextStep({
93
+ tool: searchFlights,
94
+ requires: ["destination", "departureDate"],
95
+ });
96
+ ```
97
+
98
+ ### Direct Step References
99
+
100
+ Jump to specific steps or end the route:
101
+
102
+ ```typescript
103
+ import { END_ROUTE } from "@falai/agent";
104
+
105
+ // Jump to another step
106
+ const confirm = processPayment.nextStep({
107
+ step: previousStep.getRef(), // Jump back
108
+ });
109
+
110
+ // End the route
111
+ const complete = confirm.nextStep({
112
+ step: END_ROUTE,
113
+ });
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Step Configuration
119
+
120
+ ### Configuring Initial Step
121
+
122
+ Every route has an initial step that can be configured:
123
+
124
+ ```typescript
125
+ // Option 1: Configure at route creation
126
+ const route = agent.createRoute({
127
+ title: "Booking",
128
+ initialStep: {
129
+ id: "welcome",
130
+ instructions: "Welcome to our booking system!",
131
+ collect: ["intention"],
132
+ },
133
+ });
134
+
135
+ // Option 2: Configure after creation
136
+ route.initialStep.configure({
137
+ description: "Welcome! Let's start booking",
138
+ collectFields: ["destination"],
139
+ skipIf: (data) => !!data.destination,
140
+ requires: [],
141
+ });
142
+ ```
143
+
144
+ ### Configuring Any Step
145
+
146
+ You can configure any step after creation:
147
+
148
+ ```typescript
149
+ const askName = route.initialStep.nextStep({
150
+ instructions: "What's your name?",
151
+ });
152
+
153
+ // Later, reconfigure it
154
+ askName.configure({
155
+ description: "Ask for user's full name",
156
+ collectFields: ["firstName", "lastName"],
157
+ skipIf: (data) => !!data.firstName && !!data.lastName,
158
+ });
159
+
160
+ // Chaining is supported
161
+ askName
162
+ .configure({ description: "Updated description" })
163
+ .configure({ collectFields: ["fullName"] });
164
+ ```
165
+
166
+ ### Configuration Options
167
+
168
+ ```typescript
169
+ step.configure({
170
+ // Step description
171
+ description?: string;
172
+
173
+ // Fields to collect from conversation
174
+ collectFields?: string[];
175
+
176
+ // Skip this step if condition is met
177
+ skipIf?: (data: Partial<TData>) => boolean;
178
+
179
+ // Prerequisites that must be met before entering
180
+ requires?: string[];
181
+ });
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Transitions
187
+
188
+ ### Transition Specification
189
+
190
+ Every transition from one step to another uses a `TransitionSpec`:
191
+
192
+ ```typescript
193
+ interface TransitionSpec<TData = unknown> {
194
+ // Custom step ID (optional)
195
+ id?: string;
196
+
197
+ // Chat state description
198
+ instructions?: string;
199
+
200
+ // Tool to execute
201
+ tool?: ToolRef;
202
+
203
+ // Direct step reference or END_ROUTE
204
+ step?: StepRef | symbol;
205
+
206
+ // Fields to collect in this step
207
+ collect?: string[];
208
+
209
+ // Skip condition (code-based)
210
+ skipIf?: (data: Partial<TData>) => boolean;
211
+
212
+ // Prerequisites
213
+ requires?: string[];
214
+
215
+ // AI-evaluated condition (for step selection)
216
+ condition?: string;
217
+ }
218
+ ```
219
+
220
+ ### Transition Chaining
221
+
222
+ ```typescript
223
+ // Linear flow
224
+ route.initialStep
225
+ .nextStep({
226
+ instructions: "Step 1",
227
+ collect: ["field1"],
228
+ })
229
+ .nextStep({
230
+ instructions: "Step 2",
231
+ collect: ["field2"],
232
+ })
233
+ .nextStep({
234
+ instructions: "Step 3",
235
+ collect: ["field3"],
236
+ })
237
+ .nextStep({ step: END_ROUTE });
238
+ ```
239
+
240
+ ### Branching Transitions
241
+
242
+ ```typescript
243
+ const askType = route.initialStep.nextStep({
244
+ instructions: "Are you booking a flight or hotel?",
245
+ collect: ["bookingType"],
246
+ });
247
+
248
+ // Branch 1: Flight booking
249
+ const flightFlow = askType.nextStep({
250
+ instructions: "Let's book your flight",
251
+ condition: "User selected flight",
252
+ });
253
+
254
+ // Branch 2: Hotel booking
255
+ const hotelFlow = askType.nextStep({
256
+ instructions: "Let's book your hotel",
257
+ condition: "User selected hotel",
258
+ });
259
+
260
+ // Both branches can converge later
261
+ const payment = flightFlow.nextStep({
262
+ instructions: "Let's process payment",
263
+ });
264
+
265
+ hotelFlow.nextStep({ step: payment }); // Converge to payment
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Data Collecting
271
+
272
+ ### Basic Collecting
273
+
274
+ Specify which fields to extract in each step:
275
+
276
+ ```typescript
277
+ interface UserData {
278
+ firstName: string;
279
+ lastName: string;
280
+ email: string;
281
+ phone: string;
282
+ }
283
+
284
+ const route = agent.createRoute<UserData>({ ... });
285
+
286
+ // Collect single field
287
+ const askName = route.initialStep.nextStep({
288
+ instructions: "What's your first name?",
289
+ collect: ["firstName"],
290
+ });
291
+
292
+ // Collect multiple fields at once
293
+ const askContact = askName.nextStep({
294
+ instructions: "Please provide your email and phone number",
295
+ collect: ["email", "phone"],
296
+ });
297
+ ```
298
+
299
+ ### Collecting with Schema Validation
300
+
301
+ The extraction schema validates collected data:
302
+
303
+ ```typescript
304
+ const route = agent.createRoute<UserData>({
305
+ title: "User Registration",
306
+
307
+ schema: {
308
+ type: "object",
309
+ properties: {
310
+ firstName: {
311
+ type: "string",
312
+ minLength: 1,
313
+ },
314
+ email: {
315
+ type: "string",
316
+ format: "email", // Validates email format
317
+ },
318
+ age: {
319
+ type: "number",
320
+ minimum: 18,
321
+ maximum: 120,
322
+ },
323
+ },
324
+ required: ["firstName", "email"],
325
+ },
326
+ });
327
+
328
+ // AI will extract and validate according to schema
329
+ const askInfo = route.initialStep.nextStep({
330
+ instructions: "Please provide your name, email, and age",
331
+ collect: ["firstName", "email", "age"],
332
+ });
333
+ ```
334
+
335
+ ### Conditional Collecting
336
+
337
+ Use `skipIf` to avoid re-asking for data:
338
+
339
+ ```typescript
340
+ const askDestination = route.initialStep.nextStep({
341
+ instructions: "Where would you like to go?",
342
+ collect: ["destination"],
343
+ // Skip if we already have the destination
344
+ skipIf: (data) => !!data.destination,
345
+ });
346
+
347
+ const askDates = askDestination.nextStep({
348
+ instructions: "When would you like to travel?",
349
+ collect: ["departureDate", "returnDate"],
350
+ // Skip if we have both dates
351
+ skipIf: (data) => !!data.departureDate && !!data.returnDate,
352
+ });
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Step Logic
358
+
359
+ ### Skip Conditions
360
+
361
+ Control when steps should be bypassed:
362
+
363
+ ```typescript
364
+ // Skip if data already exists
365
+ const askEmail = route.initialStep.nextStep({
366
+ instructions: "What's your email?",
367
+ collect: ["email"],
368
+ skipIf: (data) => !!data.email,
369
+ });
370
+
371
+ // Skip based on business logic
372
+ const askShipping = askEmail.nextStep({
373
+ instructions: "What's your shipping address?",
374
+ collect: ["shippingAddress"],
375
+ // Skip if user selected digital product
376
+ skipIf: (data) => data.productType === "digital",
377
+ });
378
+
379
+ // Skip based on multiple conditions
380
+ const askBilling = askShipping.nextStep({
381
+ instructions: "What's your billing address?",
382
+ collect: ["billingAddress"],
383
+ // Skip if billing same as shipping, or already provided
384
+ skipIf: (data) =>
385
+ data.billingSameAsShipping === true || !!data.billingAddress,
386
+ });
387
+ ```
388
+
389
+ ### Required Data
390
+
391
+ Ensure prerequisites are met before entering a step:
392
+
393
+ ```typescript
394
+ // Can't search without destination and dates
395
+ const searchFlights = askDates.nextStep({
396
+ tool: searchFlightsTool,
397
+ requires: ["destination", "departureDate"],
398
+ });
399
+
400
+ // Can't checkout without all required fields
401
+ const processPayment = selectFlight.nextStep({
402
+ tool: processPaymentTool,
403
+ requires: ["destination", "departureDate", "selectedFlight", "paymentMethod"],
404
+ });
405
+
406
+ // Multiple prerequisites
407
+ const generateInvoice = processPayment.nextStep({
408
+ instructions: "Here's your invoice",
409
+ requires: ["paymentConfirmation", "customerEmail", "bookingReference"],
410
+ });
411
+ ```
412
+
413
+ ### Step Conditions
414
+
415
+ AI-evaluated conditions for step selection:
416
+
417
+ ```typescript
418
+ const askIssue = route.initialStep.nextStep({
419
+ instructions: "What seems to be the problem?",
420
+ collect: ["issueDescription"],
421
+ });
422
+
423
+ // Technical support path
424
+ const technicalHelp = askIssue.nextStep({
425
+ instructions: "Let me help with your technical issue",
426
+ condition: "Issue is technical in nature",
427
+ });
428
+
429
+ // Billing support path
430
+ const billingHelp = askIssue.nextStep({
431
+ instructions: "Let me help with your billing issue",
432
+ condition: "Issue is related to billing or payments",
433
+ });
434
+
435
+ // General inquiry path
436
+ const generalHelp = askIssue.nextStep({
437
+ instructions: "Let me help with your question",
438
+ condition: "Issue is a general inquiry",
439
+ });
440
+ ```
441
+
442
+ ---
443
+
444
+ ## Step Types
445
+
446
+ ### 1. Chat states
447
+
448
+ Present information and collect data:
449
+
450
+ ```typescript
451
+ const chat = route.initialStep.nextStep({
452
+ instructions: "What would you like to know?",
453
+ collect: ["question"],
454
+ });
455
+ ```
456
+
457
+ **When to use:**
458
+
459
+ - Ask questions
460
+ - Present information
461
+ - Collect user input
462
+ - Confirm actions
463
+
464
+ ### 2. Tool states
465
+
466
+ Execute functions:
467
+
468
+ ```typescript
469
+ const tool = route.initialStep.nextStep({
470
+ tool: myTool,
471
+ requires: ["param1", "param2"],
472
+ });
473
+ ```
474
+
475
+ **When to use:**
476
+
477
+ - Call APIs
478
+ - Database operations
479
+ - Complex computations
480
+ - External integrations
481
+ - Data validation/enrichment
482
+
483
+ ### 3. Initial Step
484
+
485
+ Every route's starting point:
486
+
487
+ ```typescript
488
+ route.initialStep.configure({
489
+ description: "Welcome message",
490
+ collectFields: ["initialInput"],
491
+ });
492
+ ```
493
+
494
+ **When to configure:**
495
+
496
+ - Set up welcome messages
497
+ - Collect initial context
498
+ - Set expectations
499
+ - Pre-populate data
500
+
501
+ ### 4. End Step (Terminal Step)
502
+
503
+ Every route ends when it reaches `END_ROUTE`. You can configure what happens at completion:
504
+
505
+ #### Option A: Route-Level Configuration (Recommended)
506
+
507
+ ```typescript
508
+ import { END_ROUTE } from "@falai/agent";
509
+
510
+ const bookingRoute = agent.createRoute({
511
+ title: "Book Flight",
512
+
513
+ // Configure end step behavior
514
+ endStep: {
515
+ instructions: "Confirm booking and thank the user!",
516
+ tool: sendConfirmationEmail, // Execute final actions
517
+ collect: ["finalConfirmation"], // Collect last data
518
+ },
519
+ });
520
+
521
+ // Later, just transition to END_ROUTE
522
+ finalStep.nextStep({
523
+ step: END_ROUTE,
524
+ });
525
+ ```
526
+
527
+ #### Option B: Per-Transition Override
528
+
529
+ ```typescript
530
+ // Override endStep for this specific path
531
+ finalStep.nextStep({
532
+ instructions: "Special completion message for VIP users!",
533
+ step: END_ROUTE,
534
+ });
535
+ ```
536
+
537
+ #### Option C: Default Behavior
538
+
539
+ If you don't configure `endStep`, a smart default completion message is generated:
540
+
541
+ ```typescript
542
+ finalStep.nextStep({
543
+ step: END_ROUTE,
544
+ });
545
+ // Uses default: "Summarize what was accomplished and confirm completion..."
546
+ ```
547
+
548
+ **End Step Capabilities:**
549
+
550
+ ```typescript
551
+ endStep: {
552
+ // Completion message instruction
553
+ instructions?: string;
554
+
555
+ // Execute final actions (emails, database updates, etc.)
556
+ tool?: ToolRef;
557
+
558
+ // Collect final data before completion
559
+ collect?: string[];
560
+
561
+ // Require certain data to be present
562
+ requires?: string[];
563
+
564
+ // Custom step ID for debugging
565
+ id?: string;
566
+ }
567
+ ```
568
+
569
+ **When to use END_ROUTE:**
570
+
571
+ - ✅ Route completion - all required data collected
572
+ - ✅ Success outcomes - action completed successfully
573
+ - ✅ Failure outcomes - error handling completed
574
+ - ✅ Before transition - handoff to another route via `onComplete`
575
+
576
+ **End Step with Tools:**
577
+
578
+ Execute final actions when route completes:
579
+
580
+ ```typescript
581
+ import { defineTool, END_ROUTE } from "@falai/agent";
582
+
583
+ const notifyTeam = defineTool("notify_team", async ({ data }) => {
584
+ await slack.send({
585
+ channel: "#bookings",
586
+ message: `New booking: ${data.hotelName} for ${data.guests} guests`,
587
+ });
588
+ return { data: "Team notified" };
589
+ });
590
+
591
+ const bookingRoute = agent.createRoute({
592
+ endStep: {
593
+ tool: notifyTeam, // Runs when route completes
594
+ instructions: "Booking complete! Our team has been notified.",
595
+ },
596
+ });
597
+ ```
598
+
599
+ **Key Points:**
600
+
601
+ - ✅ Configure once at route level (DRY principle)
602
+ - ✅ Can be overridden per-transition if needed
603
+ - ✅ Supports full step capabilities: `instructions`, `tool`, `collect`, `requires`
604
+ - ✅ Automatically generates message if not configured
605
+ - ✅ Executes before `onComplete` route transitions
606
+
607
+ ---
608
+
609
+ ## Advanced Patterns
610
+
611
+ ### Pattern 1: Step Loops
612
+
613
+ ```typescript
614
+ const askItems = route.initialStep.nextStep({
615
+ instructions: "What items would you like to add to your cart?",
616
+ collect: ["newItem"],
617
+ });
618
+
619
+ const confirmMore = askItems.nextStep({
620
+ instructions: "Would you like to add more items?",
621
+ collect: ["addMore"],
622
+ });
623
+
624
+ // Loop back to askItems if user wants more
625
+ confirmMore.nextStep({
626
+ step: askItems,
627
+ condition: "User wants to add more items",
628
+ });
629
+
630
+ // Or continue to checkout
631
+ const checkout = confirmMore.nextStep({
632
+ instructions: "Let's proceed to checkout",
633
+ condition: "User is done adding items",
634
+ });
635
+ ```
636
+
637
+ ### Pattern 2: Error Handling Steps
638
+
639
+ ```typescript
640
+ const processPayment = route.initialStep.nextStep({
641
+ tool: paymentTool,
642
+ requires: ["amount", "paymentMethod"],
643
+ });
644
+
645
+ // Success path
646
+ const paymentSuccess = processPayment.nextStep({
647
+ instructions: "Payment successful! Here's your receipt",
648
+ condition: "Payment was successful",
649
+ });
650
+
651
+ // Failure path
652
+ const paymentFailed = processPayment.nextStep({
653
+ instructions:
654
+ "Payment failed. Would you like to try a different payment method?",
655
+ collect: ["retryPayment"],
656
+ condition: "Payment failed",
657
+ });
658
+
659
+ // Retry logic
660
+ paymentFailed.nextStep({
661
+ step: processPayment,
662
+ condition: "User wants to retry",
663
+ });
664
+ ```
665
+
666
+ ### Pattern 3: Progressive Disclosure
667
+
668
+ ```typescript
669
+ // Start with basic info
670
+ const askBasic = route.initialStep.nextStep({
671
+ instructions: "Let's start with the basics. What's your name?",
672
+ collect: ["name"],
673
+ });
674
+
675
+ // Reveal more options
676
+ const askPreferences = askBasic.nextStep({
677
+ instructions: "Great! Now, would you like to customize your experience?",
678
+ collect: ["wantsCustomization"],
679
+ });
680
+
681
+ // Only ask detailed questions if user wants customization
682
+ const askDetailed = askPreferences.nextStep({
683
+ instructions: "Tell me about your preferences...",
684
+ collect: ["theme", "notifications", "language"],
685
+ skipIf: (data) => data.wantsCustomization === false,
686
+ });
687
+
688
+ const finish = askDetailed.nextStep({
689
+ instructions: "All set! Your account is ready",
690
+ });
691
+
692
+ // Direct path if no customization
693
+ askPreferences.nextStep({
694
+ step: finish,
695
+ condition: "User doesn't want customization",
696
+ });
697
+ ```
698
+
699
+ ### Pattern 4: Step Validation
700
+
701
+ ```typescript
702
+ const askAge = route.initialStep.nextStep({
703
+ instructions: "How old are you?",
704
+ collect: ["age"],
705
+ });
706
+
707
+ const validateAge = askAge.nextStep({
708
+ tool: validateAgeTool,
709
+ requires: ["age"],
710
+ });
711
+
712
+ // Valid age
713
+ const proceed = validateAge.nextStep({
714
+ instructions: "Great! Let's continue",
715
+ condition: "Age is valid",
716
+ });
717
+
718
+ // Invalid age - loop back
719
+ validateAge.nextStep({
720
+ step: askAge,
721
+ condition: "Age is invalid",
722
+ });
723
+ ```
724
+
725
+ ### Pattern 5: Context-Aware Steps
726
+
727
+ ```typescript
728
+ const askQuestion = route.initialStep.nextStep({
729
+ instructions: "What would you like to know?",
730
+ collect: ["question"],
731
+ });
732
+
733
+ // Different responses based on user type
734
+ const premiumResponse = askQuestion.nextStep({
735
+ instructions: "As a premium member, here's detailed information...",
736
+ condition: "User has premium account",
737
+ });
738
+
739
+ const basicResponse = askQuestion.nextStep({
740
+ instructions: "Here's the basic information. Upgrade for more details!",
741
+ condition: "User has basic account",
742
+ });
743
+ ```
744
+
745
+ ---
746
+
747
+ ## Step Properties
748
+
749
+ ### Accessing Step Properties
750
+
751
+ ```typescript
752
+ const step = route.getStep("ask_name");
753
+
754
+ // Step identification
755
+ console.log(step.id); // "step_ask_name_abc123"
756
+ console.log(step.routeId); // "route_onboarding_xyz789"
757
+ console.log(step.description); // "Ask for user's name"
758
+
759
+ // Step configuration
760
+ console.log(step.collectFields); // ["firstName", "lastName"]
761
+ console.log(step.requires); // ["userId"]
762
+
763
+ // Step logic
764
+ if (step.skipIf) {
765
+ const shouldSkip = step.skipIf(dataData);
766
+ console.log("Should skip:", shouldSkip);
767
+ }
768
+
769
+ // Step guidelines
770
+ step.addGuideline({
771
+ condition: "User provides invalid name",
772
+ action: "Ask for valid name format",
773
+ });
774
+ console.log(step.getGuidelines());
775
+
776
+ // Transitions
777
+ console.log(step.getTransitions()); // [Transition, Transition, ...]
778
+ ```
779
+
780
+ ### Step Reference
781
+
782
+ ```typescript
783
+ const stepRef = step.getRef();
784
+ console.log(stepRef);
785
+ // { id: "step_ask_name_abc123", routeId: "route_onboarding_xyz789" }
786
+
787
+ // Use reference in transitions
788
+ anotherStep.nextStep({ step: stepRef });
789
+ ```
790
+
791
+ ---
792
+
793
+ ## Best Practices
794
+
795
+ ### ✅ Do's
796
+
797
+ - **Use descriptive instructions** - Clear prompts for better UX
798
+ - **Define collect fields** - Explicit data extraction
799
+ - **Use skipIf for efficiency** - Avoid redundant questions
800
+ - **Set requires** - Prevent premature execution
801
+ - **Configure initial step** - Proper route entry point
802
+ - **Use tool states for logic** - Separate concerns
803
+ - **Add step-specific guidelines** - Context-aware behavior
804
+
805
+ ### ❌ Don'ts
806
+
807
+ - **Don't hardcode step IDs** - Let framework generate them
808
+ - **Don't skip validation** - Always validate with requires
809
+ - **Don't create circular loops** - Without exit conditions
810
+ - **Don't collect unrelated data** - One concept per step
811
+ - **Don't use vague conditions** - Be specific
812
+ - **Don't forget skipIf** - For pre-populated data
813
+ - **Don't mix chat and tool** - One type per step
814
+
815
+ ---
816
+
817
+ ## Troubleshooting
818
+
819
+ ### Step Not Being Entered
820
+
821
+ ```typescript
822
+ // ❌ Problem: Required data missing
823
+ const step = prev.nextStep({
824
+ instructions: "Do something",
825
+ requires: ["field1", "field2"], // Missing field2
826
+ });
827
+
828
+ // ✅ Solution: Ensure required data is collected first
829
+ const collectData = prev.nextStep({
830
+ instructions: "Collect data",
831
+ collect: ["field1", "field2"],
832
+ });
833
+
834
+ const step = collectData.nextStep({
835
+ instructions: "Do something",
836
+ requires: ["field1", "field2"], // Now available
837
+ });
838
+ ```
839
+
840
+ ### Step Always Skipped
841
+
842
+ ```typescript
843
+ // ❌ Problem: skipIf always returns true
844
+ const step = prev.nextStep({
845
+ instructions: "Ask something",
846
+ skipIf: (data) => true, // Always skips!
847
+ });
848
+
849
+ // ✅ Solution: Use proper condition
850
+ const step = prev.nextStep({
851
+ instructions: "Ask something",
852
+ skipIf: (data) => !!data.field, // Only skip if field exists
853
+ });
854
+ ```
855
+
856
+ ### Data Not Being Collected
857
+
858
+ ```typescript
859
+ // ❌ Problem: Forgot to specify collect
860
+ const step = prev.nextStep({
861
+ instructions: "What's your name?",
862
+ // Missing collect!
863
+ });
864
+
865
+ // ✅ Solution: Add collect fields
866
+ const step = prev.nextStep({
867
+ instructions: "What's your name?",
868
+ collect: ["firstName", "lastName"],
869
+ });
870
+ ```
871
+
872
+ ---
873
+
874
+ ## See Also
875
+
876
+ - [Routes Guide](./ROUTES.md) - Understanding routes
877
+ - [API Reference - Step](./API_REFERENCE.md#step) - Complete API docs
878
+ - [Examples](../examples/) - Real-world implementations
879
+ - [Architecture Guide](./ARCHITECTURE.md) - System overview
880
+
881
+ ---
882
+
883
+ **Made with ❤️ for the community**