@falai/agent 0.6.9 → 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
@@ -1,4 +1,4 @@
1
- import { enterRoute, mergeExtracted } from "../types/session";
1
+ import { enterRoute, mergeCollected } from "../types/session";
2
2
  import { PromptComposer } from "./PromptComposer";
3
3
  import { getLastMessageFromHistory } from "../utils/event";
4
4
  import { logger } from "../utils/logger";
@@ -8,10 +8,10 @@ export class RoutingEngine {
8
8
  }
9
9
  /**
10
10
  * Optimized decision for single-route scenarios
11
- * Skips route scoring and only does state selection
11
+ * Skips route scoring and only does step selection
12
12
  * @private
13
13
  */
14
- async decideSingleRouteState(params) {
14
+ async decideSingleRouteStep(params) {
15
15
  const { route, session, history, agentMeta, ai, context, signal } = params;
16
16
  let updatedSession = session;
17
17
  const selectedRoute = route;
@@ -19,237 +19,237 @@ export class RoutingEngine {
19
19
  if (!session.currentRoute || session.currentRoute.id !== route.id) {
20
20
  updatedSession = enterRoute(session, route.id, route.title);
21
21
  if (route.initialData) {
22
- updatedSession = mergeExtracted(updatedSession, route.initialData);
22
+ updatedSession = mergeCollected(updatedSession, route.initialData);
23
23
  logger.debug(`[RoutingEngine] Single-route: Merged initial data:`, route.initialData);
24
24
  }
25
25
  logger.debug(`[RoutingEngine] Single-route: Entered route: ${route.title}`);
26
26
  }
27
- // Get candidate states
28
- const currentState = updatedSession.currentState
29
- ? route.getState(updatedSession.currentState.id)
27
+ // Get candidate steps
28
+ const currentStep = updatedSession.currentStep
29
+ ? route.getStep(updatedSession.currentStep.id)
30
30
  : undefined;
31
- const candidates = this.getCandidateStates(route, currentState, updatedSession.extracted || {});
31
+ const candidates = this.getCandidateSteps(route, currentStep, updatedSession.data || {});
32
32
  if (candidates.length === 0) {
33
- logger.warn(`[RoutingEngine] Single-route: No valid states found`);
33
+ logger.warn(`[RoutingEngine] Single-route: No valid steps found`);
34
34
  return { selectedRoute, session: updatedSession };
35
35
  }
36
36
  // If only one candidate, check if route is complete
37
37
  if (candidates.length === 1) {
38
38
  const isRouteComplete = candidates[0].isRouteComplete;
39
39
  if (isRouteComplete) {
40
- logger.debug(`[RoutingEngine] Single-route: Route complete - all data collected, END_STATE reached`);
41
- // Don't return a selectedState when route is complete - there's no state to enter
40
+ logger.debug(`[RoutingEngine] Single-route: Route complete - all data collected, END_ROUTE reached`);
41
+ // Don't return a selectedStep when route is complete - there's no step to enter
42
42
  return {
43
43
  selectedRoute,
44
- selectedState: undefined,
44
+ selectedStep: undefined,
45
45
  session: updatedSession,
46
46
  isRouteComplete: true,
47
47
  };
48
48
  }
49
49
  else {
50
- logger.debug(`[RoutingEngine] Single-route: Only one valid state: ${candidates[0].state.id}`);
50
+ logger.debug(`[RoutingEngine] Single-route: Only one valid step: ${candidates[0].step.id}`);
51
51
  return {
52
52
  selectedRoute,
53
- selectedState: candidates[0].state,
53
+ selectedStep: candidates[0].step,
54
54
  session: updatedSession,
55
55
  isRouteComplete: false,
56
56
  };
57
57
  }
58
58
  }
59
- // Multiple candidates - use AI to select best state
59
+ // Multiple candidates - use AI to select best step
60
60
  const lastUserMessage = getLastMessageFromHistory(history);
61
- const statePrompt = this.buildStateSelectionPrompt(route, currentState, candidates, updatedSession.extracted || {}, history, lastUserMessage, agentMeta);
62
- const stateSchema = this.buildStateSelectionSchema(candidates.map((c) => c.state.id));
63
- const stateResult = await ai.generateMessage({
64
- prompt: statePrompt,
61
+ const stepPrompt = this.buildStepSelectionPrompt(route, currentStep, candidates, updatedSession.data || {}, history, lastUserMessage, agentMeta);
62
+ const stepSchema = this.buildStepSelectionSchema(candidates.map((c) => c.step.id));
63
+ const stepResult = await ai.generateMessage({
64
+ prompt: stepPrompt,
65
65
  history,
66
66
  context,
67
67
  signal,
68
68
  parameters: {
69
- jsonSchema: stateSchema,
70
- schemaName: "state_selection",
69
+ jsonSchema: stepSchema,
70
+ schemaName: "step_selection",
71
71
  },
72
72
  });
73
- const selectedStateId = stateResult.structured?.selectedStateId;
74
- const selectedState = candidates.find((c) => c.state.id === selectedStateId)?.state;
75
- if (selectedState) {
76
- logger.debug(`[RoutingEngine] Single-route: AI selected state: ${selectedState.id}`);
77
- logger.debug(`[RoutingEngine] Single-route: Reasoning: ${stateResult.structured?.reasoning}`);
73
+ const selectedStepId = stepResult.structured?.selectedStepId;
74
+ const selectedStep = candidates.find((c) => c.step.id === selectedStepId)?.step;
75
+ if (selectedStep) {
76
+ logger.debug(`[RoutingEngine] Single-route: AI selected step: ${selectedStep.id}`);
77
+ logger.debug(`[RoutingEngine] Single-route: Reasoning: ${stepResult.structured?.reasoning}`);
78
78
  }
79
79
  else {
80
- logger.warn(`[RoutingEngine] Single-route: Invalid state ID returned, using first candidate`);
80
+ logger.warn(`[RoutingEngine] Single-route: Invalid step ID returned, using first candidate`);
81
81
  }
82
82
  return {
83
83
  selectedRoute,
84
- selectedState: selectedState || candidates[0].state,
85
- responseDirectives: stateResult.structured?.responseDirectives,
84
+ selectedStep: selectedStep || candidates[0].step,
85
+ responseDirectives: stepResult.structured?.responseDirectives,
86
86
  session: updatedSession,
87
87
  };
88
88
  }
89
89
  /**
90
- * Recursively traverse state chain to find first non-skipped state or END_STATE
90
+ * Recursively traverse step chain to find first non-skipped step or END_ROUTE
91
91
  * @private
92
92
  */
93
- findFirstValidStateRecursive(currentState, extracted, visited) {
93
+ findFirstValidStepRecursive(currentStep, data, visited) {
94
94
  // Prevent infinite loops
95
- if (visited.has(currentState.id)) {
95
+ if (visited.has(currentStep.id)) {
96
96
  return {};
97
97
  }
98
- visited.add(currentState.id);
99
- const transitions = currentState.getTransitions();
98
+ visited.add(currentStep.id);
99
+ const transitions = currentStep.getTransitions();
100
100
  for (const transition of transitions) {
101
101
  const target = transition.getTarget();
102
- // Check for END_STATE transition
102
+ // Check for END_ROUTE transition
103
103
  if (!target &&
104
- transition.spec.state &&
105
- typeof transition.spec.state === "symbol") {
106
- // Found END_STATE - route is complete
104
+ transition.spec.step &&
105
+ typeof transition.spec.step === "symbol") {
106
+ // Found END_ROUTE - route is complete
107
107
  return {
108
- isRouteComplete: true
108
+ isRouteComplete: true,
109
109
  };
110
110
  }
111
111
  if (!target)
112
112
  continue;
113
- // If target should NOT be skipped, we found our state
114
- if (!target.shouldSkip(extracted)) {
115
- logger.debug(`[RoutingEngine] Found valid state after skipping: ${target.id}`);
113
+ // If target should NOT be skipped, we found our step
114
+ if (!target.shouldSkip(data)) {
115
+ logger.debug(`[RoutingEngine] Found valid step after skipping: ${target.id}`);
116
116
  return {
117
- state: target,
117
+ step: target,
118
118
  condition: transition.condition,
119
119
  };
120
120
  }
121
121
  // Target should be skipped too - recurse deeper
122
- logger.debug(`[RoutingEngine] Skipping state ${target.id} (skipIf condition met), continuing traversal...`);
123
- const result = this.findFirstValidStateRecursive(target, extracted, visited);
124
- // If we found something (a valid state or END_STATE), return it
125
- if (result.state || result.isRouteComplete) {
122
+ logger.debug(`[RoutingEngine] Skipping step ${target.id} (skipIf condition met), continuing traversal...`);
123
+ const result = this.findFirstValidStepRecursive(target, data, visited);
124
+ // If we found something (a valid step or END_ROUTE), return it
125
+ if (result.step || result.isRouteComplete) {
126
126
  return result;
127
127
  }
128
128
  }
129
- // No valid states or END_STATE found in this branch
129
+ // No valid steps or END_ROUTE found in this branch
130
130
  return {};
131
131
  }
132
132
  /**
133
- * Identify valid next candidate states based on current state and extracted data
134
- * Returns state with isRouteComplete flag if route is complete (all states skipped + has END_STATE transition)
133
+ * Identify valid next candidate steps based on current step and collected data
134
+ * Returns step with isRouteComplete flag if route is complete (all steps skipped + has END_ROUTE transition)
135
135
  */
136
- getCandidateStates(route, currentState, extracted) {
136
+ getCandidateSteps(route, currentStep, data) {
137
137
  const candidates = [];
138
- if (!currentState) {
139
- const initialState = route.initialState;
140
- if (initialState.shouldSkip(extracted)) {
141
- // Initial state should be skipped - recursively traverse to find first non-skipped state or END_STATE
142
- const result = this.findFirstValidStateRecursive(initialState, extracted, new Set());
138
+ if (!currentStep) {
139
+ const initialStep = route.initialStep;
140
+ if (initialStep.shouldSkip(data)) {
141
+ // Initial step should be skipped - recursively traverse to find first non-skipped step or END_ROUTE
142
+ const result = this.findFirstValidStepRecursive(initialStep, data, new Set());
143
143
  if (result.isRouteComplete) {
144
- // All states are skipped and we reached END_STATE
145
- logger.debug(`[RoutingEngine] Route complete on entry: all states skipped, END_STATE reached`);
144
+ // All steps are skipped and we reached END_ROUTE
145
+ logger.debug(`[RoutingEngine] Route complete on entry: all steps skipped, END_ROUTE reached`);
146
146
  return [
147
147
  {
148
- state: initialState,
148
+ step: initialStep,
149
149
  condition: "Route complete - all data collected on entry",
150
150
  isRouteComplete: true,
151
151
  },
152
152
  ];
153
153
  }
154
- else if (result.state) {
155
- // Found a non-skipped state
154
+ else if (result.step) {
155
+ // Found a non-skipped step
156
156
  candidates.push({
157
- state: result.state,
157
+ step: result.step,
158
158
  condition: result.condition,
159
- requiredData: result.state.requiredData,
160
- gatherFields: result.state.gatherFields,
159
+ requires: result.step.requires,
160
+ collectFields: result.step.collectFields,
161
161
  });
162
162
  }
163
- // If no state found and not complete, fall through to return empty candidates
163
+ // If no step found and not complete, fall through to return empty candidates
164
164
  }
165
165
  else {
166
166
  candidates.push({
167
- state: initialState,
168
- requiredData: initialState.requiredData,
169
- gatherFields: initialState.gatherFields,
167
+ step: initialStep,
168
+ requires: initialStep.requires,
169
+ collectFields: initialStep.collectFields,
170
170
  });
171
171
  }
172
172
  return candidates;
173
173
  }
174
- const transitions = currentState.getTransitions();
174
+ const transitions = currentStep.getTransitions();
175
175
  let hasEndRoute = false;
176
176
  for (const transition of transitions) {
177
177
  const target = transition.getTarget();
178
- // Check for END_STATE transition (no target state)
178
+ // Check for END_ROUTE transition (no target step)
179
179
  if (!target &&
180
- transition.spec.state &&
181
- typeof transition.spec.state === "symbol") {
180
+ transition.spec.step &&
181
+ typeof transition.spec.step === "symbol") {
182
182
  hasEndRoute = true;
183
183
  continue;
184
184
  }
185
185
  if (!target)
186
186
  continue;
187
- if (target.shouldSkip(extracted)) {
188
- logger.debug(`[RoutingEngine] Skipping state ${target.id} (skipIf condition met)`);
189
- // Recursively traverse to find next valid state or END_STATE
190
- const result = this.findFirstValidStateRecursive(target, extracted, new Set([currentState.id]) // Already visited current state
187
+ if (target.shouldSkip(data)) {
188
+ logger.debug(`[RoutingEngine] Skipping step ${target.id} (skipIf condition met)`);
189
+ // Recursively traverse to find next valid step or END_ROUTE
190
+ const result = this.findFirstValidStepRecursive(target, data, new Set([currentStep.id]) // Already visited current step
191
191
  );
192
192
  if (result.isRouteComplete) {
193
193
  hasEndRoute = true;
194
194
  }
195
- else if (result.state) {
196
- // Found a non-skipped state deeper in the chain
195
+ else if (result.step) {
196
+ // Found a non-skipped step deeper in the chain
197
197
  candidates.push({
198
- state: result.state,
198
+ step: result.step,
199
199
  condition: result.condition || transition.condition,
200
- requiredData: result.state.requiredData,
201
- gatherFields: result.state.gatherFields,
200
+ requires: result.step.requires,
201
+ collectFields: result.step.collectFields,
202
202
  });
203
203
  }
204
204
  continue;
205
205
  }
206
206
  candidates.push({
207
- state: target,
207
+ step: target,
208
208
  condition: transition.condition,
209
- requiredData: target.requiredData,
210
- gatherFields: target.gatherFields,
209
+ requires: target.requires,
210
+ collectFields: target.collectFields,
211
211
  });
212
212
  }
213
213
  // If no valid candidates found
214
214
  if (candidates.length === 0) {
215
- // If current state has END_STATE transition, the route is complete
215
+ // If current step has END_ROUTE transition, the route is complete
216
216
  if (hasEndRoute) {
217
- logger.debug(`[RoutingEngine] Route complete: all states processed, END_STATE reached`);
218
- // Return current state with completion flag
217
+ logger.debug(`[RoutingEngine] Route complete: all steps processed, END_ROUTE reached`);
218
+ // Return current step with completion flag
219
219
  return [
220
220
  {
221
- state: currentState,
221
+ step: currentStep,
222
222
  condition: "Route complete - all data collected",
223
223
  isRouteComplete: true,
224
224
  },
225
225
  ];
226
226
  }
227
- // Otherwise, stay in current state if it's still valid
228
- if (!currentState.shouldSkip(extracted)) {
227
+ // Otherwise, stay in current step if it's still valid
228
+ if (!currentStep.shouldSkip(data)) {
229
229
  candidates.push({
230
- state: currentState,
231
- condition: "Continue in current state (no valid transitions)",
232
- requiredData: currentState.requiredData,
233
- gatherFields: currentState.gatherFields,
230
+ step: currentStep,
231
+ condition: "Continue in current step (no valid transitions)",
232
+ requires: currentStep.requires,
233
+ collectFields: currentStep.collectFields,
234
234
  });
235
235
  }
236
236
  }
237
237
  return candidates;
238
238
  }
239
239
  /**
240
- * Full routing orchestration: builds prompt and schema, calls AI, selects route/state,
240
+ * Full routing orchestration: builds prompt and schema, calls AI, selects route/step,
241
241
  * and updates the session (including initialData merge when entering a new route).
242
242
  *
243
- * OPTIMIZATION: If there's only 1 route, skips route scoring and only does state selection.
243
+ * OPTIMIZATION: If there's only 1 route, skips route scoring and only does step selection.
244
244
  */
245
- async decideRouteAndState(params) {
245
+ async decideRouteAndStep(params) {
246
246
  const { routes, session, history, agentMeta, ai, context, signal } = params;
247
247
  if (routes.length === 0) {
248
248
  return { session };
249
249
  }
250
- // OPTIMIZATION: Single route - skip route scoring, only do state selection
250
+ // OPTIMIZATION: Single route - skip route scoring, only do step selection
251
251
  if (routes.length === 1) {
252
- return this.decideSingleRouteState({
252
+ return this.decideSingleRouteStep({
253
253
  route: routes[0],
254
254
  session,
255
255
  history,
@@ -260,37 +260,37 @@ export class RoutingEngine {
260
260
  });
261
261
  }
262
262
  const lastUserMessage = getLastMessageFromHistory(history);
263
- let activeRouteStates;
263
+ let activeRouteSteps;
264
264
  let activeRoute;
265
265
  let isRouteComplete = false;
266
266
  if (session.currentRoute) {
267
267
  activeRoute = routes.find((r) => r.id === session.currentRoute?.id);
268
268
  if (activeRoute) {
269
- const currentState = session.currentState
270
- ? activeRoute.getState(session.currentState.id)
269
+ const currentStep = session.currentStep
270
+ ? activeRoute.getStep(session.currentStep.id)
271
271
  : undefined;
272
- const candidates = this.getCandidateStates(activeRoute, currentState, session.extracted || {});
272
+ const candidates = this.getCandidateSteps(activeRoute, currentStep, session.data || {});
273
273
  // Check if route is complete
274
274
  if (candidates.length === 1 && candidates[0].isRouteComplete) {
275
275
  isRouteComplete = true;
276
276
  logger.debug(`[RoutingEngine] Route ${activeRoute.title} is complete - all data collected`);
277
- // Don't include states in routing if route is complete
278
- activeRouteStates = undefined;
277
+ // Don't include steps in routing if route is complete
278
+ activeRouteSteps = undefined;
279
279
  }
280
280
  else {
281
- activeRouteStates = candidates.map((c) => ({
282
- stateId: c.state.id,
283
- description: c.state.description || "",
281
+ activeRouteSteps = candidates.map((c) => ({
282
+ stepId: c.step.id,
283
+ description: c.step.description || "",
284
284
  condition: c.condition,
285
- requiredData: c.requiredData,
286
- gatherFields: c.gatherFields,
285
+ requires: c.requires,
286
+ collectFields: c.collectFields,
287
287
  }));
288
- logger.debug(`[RoutingEngine] Found ${activeRouteStates.length} candidate states for active route`);
288
+ logger.debug(`[RoutingEngine] Found ${activeRouteSteps.length} candidate steps for active route`);
289
289
  }
290
290
  }
291
291
  }
292
- const routingSchema = this.buildDynamicRoutingSchema(routes, undefined, activeRouteStates);
293
- const routingPrompt = this.buildRoutingPrompt(history, routes, lastUserMessage, agentMeta, session, activeRouteStates);
292
+ const routingSchema = this.buildDynamicRoutingSchema(routes, undefined, activeRouteSteps);
293
+ const routingPrompt = this.buildRoutingPrompt(history, routes, lastUserMessage, agentMeta, session, activeRouteSteps);
294
294
  const routingResult = await ai.generateMessage({
295
295
  prompt: routingPrompt,
296
296
  history,
@@ -302,7 +302,7 @@ export class RoutingEngine {
302
302
  },
303
303
  });
304
304
  let selectedRoute;
305
- let selectedState;
305
+ let selectedStep;
306
306
  let responseDirectives;
307
307
  let updatedSession = session;
308
308
  if (routingResult.structured?.routes) {
@@ -314,12 +314,12 @@ export class RoutingEngine {
314
314
  selectedRoute = routes.find((r) => r.id === decision.routeId);
315
315
  responseDirectives = routingResult.structured.responseDirectives;
316
316
  if (selectedRoute === activeRoute &&
317
- routingResult.structured.selectedStateId &&
317
+ routingResult.structured.selectedStepId &&
318
318
  activeRoute) {
319
- selectedState = activeRoute.getState(routingResult.structured.selectedStateId);
320
- if (selectedState) {
321
- logger.debug(`[RoutingEngine] AI selected state: ${selectedState.id} in active route`);
322
- logger.debug(`[RoutingEngine] State reasoning: ${routingResult.structured.stateReasoning}`);
319
+ selectedStep = activeRoute.getStep(routingResult.structured.selectedStepId);
320
+ if (selectedStep) {
321
+ logger.debug(`[RoutingEngine] AI selected step: ${selectedStep.id} in active route`);
322
+ logger.debug(`[RoutingEngine] Step reasoning: ${routingResult.structured.stepReasoning}`);
323
323
  }
324
324
  }
325
325
  if (selectedRoute) {
@@ -328,7 +328,7 @@ export class RoutingEngine {
328
328
  session.currentRoute.id !== selectedRoute.id) {
329
329
  updatedSession = enterRoute(session, selectedRoute.id, selectedRoute.title);
330
330
  if (selectedRoute.initialData) {
331
- updatedSession = mergeExtracted(updatedSession, selectedRoute.initialData);
331
+ updatedSession = mergeCollected(updatedSession, selectedRoute.initialData);
332
332
  logger.debug(`[RoutingEngine] Merged initial data:`, selectedRoute.initialData);
333
333
  }
334
334
  logger.debug(`[RoutingEngine] Entered route: ${selectedRoute.title}`);
@@ -337,17 +337,17 @@ export class RoutingEngine {
337
337
  }
338
338
  return {
339
339
  selectedRoute,
340
- selectedState,
340
+ selectedStep,
341
341
  responseDirectives,
342
342
  session: updatedSession,
343
343
  isRouteComplete,
344
344
  };
345
345
  }
346
346
  /**
347
- * Build prompt for state selection within a single route
347
+ * Build prompt for step selection within a single route
348
348
  * @private
349
349
  */
350
- buildStateSelectionPrompt(route, currentState, candidates, extracted, history, lastMessage, agentMeta) {
350
+ buildStepSelectionPrompt(route, currentStep, candidates, data, history, lastMessage, agentMeta) {
351
351
  const pc = new PromptComposer();
352
352
  // Add agent metadata
353
353
  if (agentMeta?.name || agentMeta?.goal || agentMeta?.description) {
@@ -361,79 +361,79 @@ export class RoutingEngine {
361
361
  pc.addPersonality(personality);
362
362
  // Add route context
363
363
  pc.addInstruction(`Active Route: ${route.title}\nDescription: ${route.description || "N/A"}`);
364
- // Add current state context
365
- if (currentState) {
366
- pc.addInstruction(`Current State: ${currentState.id}\nDescription: ${currentState.description || "N/A"}`);
364
+ // Add current step context
365
+ if (currentStep) {
366
+ pc.addInstruction(`Current Step: ${currentStep.id}\nDescription: ${currentStep.description || "N/A"}`);
367
367
  }
368
368
  else {
369
- pc.addInstruction("Current State: None (entering route)");
369
+ pc.addInstruction("Current Step: None (entering route)");
370
370
  }
371
- // Add extracted data context
372
- if (Object.keys(extracted).length > 0) {
373
- pc.addInstruction(`Extracted Data So Far:\n${JSON.stringify(extracted, null, 2)}`);
371
+ // Add collected data context
372
+ if (Object.keys(data).length > 0) {
373
+ pc.addInstruction(`Collected Data So Far:\n${JSON.stringify(data, null, 2)}`);
374
374
  }
375
375
  else {
376
- pc.addInstruction("Extracted Data: None yet");
376
+ pc.addInstruction("Collected Data: None yet");
377
377
  }
378
378
  // Add conversation history
379
379
  pc.addInteractionHistory(history);
380
380
  pc.addLastMessage(lastMessage);
381
- // Add candidate states
382
- const stateDescriptions = candidates.map((candidate, idx) => {
381
+ // Add candidate steps
382
+ const stepDescriptions = candidates.map((candidate, idx) => {
383
383
  const parts = [
384
- `${idx + 1}. State ID: ${candidate.state.id}`,
385
- ` Description: ${candidate.state.description || "N/A"}`,
384
+ `${idx + 1}. Step ID: ${candidate.step.id}`,
385
+ ` Description: ${candidate.step.description || "N/A"}`,
386
386
  ];
387
387
  if (candidate.condition) {
388
388
  parts.push(` Condition: ${candidate.condition}`);
389
389
  }
390
- if (candidate.requiredData && candidate.requiredData.length > 0) {
391
- parts.push(` Required Data: ${candidate.requiredData.join(", ")}`);
390
+ if (candidate.requires && candidate.requires.length > 0) {
391
+ parts.push(` Required Data: ${candidate.requires.join(", ")}`);
392
392
  }
393
- if (candidate.gatherFields && candidate.gatherFields.length > 0) {
394
- parts.push(` Gathers: ${candidate.gatherFields.join(", ")}`);
393
+ if (candidate.collectFields && candidate.collectFields.length > 0) {
394
+ parts.push(` Collects: ${candidate.collectFields.join(", ")}`);
395
395
  }
396
396
  return parts.join("\n");
397
397
  });
398
- pc.addInstruction(`Available States to Transition To:\n${stateDescriptions.join("\n\n")}`);
398
+ pc.addInstruction(`Available Steps to Transition To:\n${stepDescriptions.join("\n\n")}`);
399
399
  // Add decision instructions
400
400
  pc.addInstruction([
401
- "Task: Decide which state to transition to based on:",
401
+ "Task: Decide which step to transition to based on:",
402
402
  "1. The user's current message and intent",
403
403
  "2. The conversation history and context",
404
- "3. The extracted data we already have",
405
- "4. The conditions and requirements of each state",
404
+ "3. The collected data we already have",
405
+ "4. The conditions and requirements of each step",
406
406
  "5. The logical flow of the conversation",
407
407
  "",
408
408
  "Rules:",
409
- "- If a state has a condition, evaluate whether it's met based on context",
410
- "- If a state requires data we don't have, consider if we should gather it now",
411
- "- Choose the state that makes the most sense for moving the conversation forward",
412
- "- States with skipIf conditions that are met have already been filtered out",
409
+ "- If a step has a condition, evaluate whether it's met based on context",
410
+ "- If a step requires data we don't have, consider if we should collect it now",
411
+ "- Choose the step that makes the most sense for moving the conversation forward",
412
+ "- Steps with skipIf conditions that are met have already been filtered out",
413
413
  "",
414
414
  "Return ONLY JSON matching the provided schema.",
415
415
  ].join("\n"));
416
416
  return pc.build();
417
417
  }
418
418
  /**
419
- * Build schema for state selection
419
+ * Build schema for step selection
420
420
  * @private
421
421
  */
422
- buildStateSelectionSchema(validStateIds) {
422
+ buildStepSelectionSchema(validStepIds) {
423
423
  return {
424
- description: "State transition decision based on conversation context and extracted data",
424
+ description: "Step transition decision based on conversation context and collected data",
425
425
  type: "object",
426
426
  properties: {
427
427
  reasoning: {
428
428
  type: "string",
429
429
  nullable: false,
430
- description: "Brief explanation of why this state was selected",
430
+ description: "Brief explanation of why this step was selected",
431
431
  },
432
- selectedStateId: {
432
+ selectedStepId: {
433
433
  type: "string",
434
434
  nullable: false,
435
- description: "The ID of the selected state to transition to",
436
- enum: validStateIds,
435
+ description: "The ID of the selected step to transition to",
436
+ enum: validStepIds,
437
437
  },
438
438
  responseDirectives: {
439
439
  type: "array",
@@ -441,11 +441,11 @@ export class RoutingEngine {
441
441
  description: "Optional bullet points the response should address (concise)",
442
442
  },
443
443
  },
444
- required: ["reasoning", "selectedStateId"],
444
+ required: ["reasoning", "selectedStepId"],
445
445
  additionalProperties: false,
446
446
  };
447
447
  }
448
- buildDynamicRoutingSchema(routes, extrasSchema, activeRouteStates) {
448
+ buildDynamicRoutingSchema(routes, extrasSchema, activeRouteSteps) {
449
449
  const routeIds = routes.map((r) => r.id);
450
450
  const routeProperties = {};
451
451
  for (const id of routeIds) {
@@ -482,24 +482,24 @@ export class RoutingEngine {
482
482
  required: ["context", "routes"],
483
483
  additionalProperties: false,
484
484
  };
485
- // Add state selection fields if there's an active route with states
486
- if (activeRouteStates && activeRouteStates.length > 0) {
485
+ // Add step selection fields if there's an active route with steps
486
+ if (activeRouteSteps && activeRouteSteps.length > 0) {
487
487
  base.properties = base.properties || {};
488
- base.properties.selectedStateId = {
488
+ base.properties.selectedStepId = {
489
489
  type: "string",
490
490
  nullable: false,
491
- description: "The state ID to transition to within the active route (required if continuing in current route)",
492
- enum: activeRouteStates.map((s) => s.stateId),
491
+ description: "The step ID to transition to within the active route (required if continuing in current route)",
492
+ enum: activeRouteSteps.map((s) => s.stepId),
493
493
  };
494
- base.properties.stateReasoning = {
494
+ base.properties.stepReasoning = {
495
495
  type: "string",
496
496
  nullable: false,
497
- description: "Brief explanation of why this state was selected",
497
+ description: "Brief explanation of why this step was selected",
498
498
  };
499
499
  base.required = [
500
500
  ...(base.required || []),
501
- "selectedStateId",
502
- "stateReasoning",
501
+ "selectedStepId",
502
+ "stepReasoning",
503
503
  ];
504
504
  }
505
505
  if (extrasSchema) {
@@ -508,7 +508,7 @@ export class RoutingEngine {
508
508
  }
509
509
  return base;
510
510
  }
511
- buildRoutingPrompt(history, routes, lastMessage, agentMeta, session, activeRouteStates) {
511
+ buildRoutingPrompt(history, routes, lastMessage, agentMeta, session, activeRouteSteps) {
512
512
  const pc = new PromptComposer();
513
513
  if (agentMeta?.name || agentMeta?.goal || agentMeta?.description) {
514
514
  pc.addAgentMeta({
@@ -526,45 +526,45 @@ export class RoutingEngine {
526
526
  "Current conversation context:",
527
527
  `- Active route: ${session.currentRoute.title} (${session.currentRoute.id})`,
528
528
  ];
529
- if (session.currentState) {
530
- sessionInfo.push(`- Current state: ${session.currentState.id}`);
531
- if (session.currentState.description) {
532
- sessionInfo.push(` "${session.currentState.description}"`);
529
+ if (session.currentStep) {
530
+ sessionInfo.push(`- Current step: ${session.currentStep.id}`);
531
+ if (session.currentStep.description) {
532
+ sessionInfo.push(` "${session.currentStep.description}"`);
533
533
  }
534
534
  }
535
- if (session.extracted && Object.keys(session.extracted).length > 0) {
536
- sessionInfo.push(`- Extracted data: ${JSON.stringify(session.extracted)}`);
535
+ if (session.data && Object.keys(session.data).length > 0) {
536
+ sessionInfo.push(`- Collected data: ${JSON.stringify(session.data)}`);
537
537
  }
538
538
  sessionInfo.push("Note: User is mid-conversation. They may want to continue current route or switch to a new one based on their intent.");
539
539
  pc.addInstruction(sessionInfo.join("\n"));
540
- // Add available states for the active route
541
- if (activeRouteStates && activeRouteStates.length > 0) {
542
- const stateInfo = [
540
+ // Add available steps for the active route
541
+ if (activeRouteSteps && activeRouteSteps.length > 0) {
542
+ const stepInfo = [
543
543
  "",
544
- "Available states in active route (choose one to transition to):",
544
+ "Available steps in active route (choose one to transition to):",
545
545
  ];
546
- activeRouteStates.forEach((state, idx) => {
547
- stateInfo.push(`${idx + 1}. State: ${state.stateId}`);
548
- if (state.description) {
549
- stateInfo.push(` Description: ${state.description}`);
546
+ activeRouteSteps.forEach((step, idx) => {
547
+ stepInfo.push(`${idx + 1}. Step: ${step.stepId}`);
548
+ if (step.description) {
549
+ stepInfo.push(` Description: ${step.description}`);
550
550
  }
551
- if (state.condition) {
552
- stateInfo.push(` Condition: ${state.condition}`);
551
+ if (step.condition) {
552
+ stepInfo.push(` Condition: ${step.condition}`);
553
553
  }
554
- if (state.requiredData && state.requiredData.length > 0) {
555
- stateInfo.push(` Required data: ${state.requiredData.join(", ")}`);
554
+ if (step.requires && step.requires.length > 0) {
555
+ stepInfo.push(` Required data: ${step.requires.join(", ")}`);
556
556
  }
557
- if (state.gatherFields && state.gatherFields.length > 0) {
558
- stateInfo.push(` Will gather: ${state.gatherFields.join(", ")}`);
557
+ if (step.collectFields && step.collectFields.length > 0) {
558
+ stepInfo.push(` Will collect: ${step.collectFields.join(", ")}`);
559
559
  }
560
560
  });
561
- stateInfo.push("");
562
- stateInfo.push("IMPORTANT: You MUST select a state to transition to. Evaluate which state makes the most sense based on:");
563
- stateInfo.push("- The conversation flow and what's been collected");
564
- stateInfo.push("- What data is still needed vs already present");
565
- stateInfo.push("- The logical next step in the conversation");
566
- stateInfo.push("- Whether conditions for states are met");
567
- pc.addInstruction(stateInfo.join("\n"));
561
+ stepInfo.push("");
562
+ stepInfo.push("IMPORTANT: You MUST select a step to transition to. Evaluate which step makes the most sense based on:");
563
+ stepInfo.push("- The conversation flow and what's been collected");
564
+ stepInfo.push("- What data is still needed vs already present");
565
+ stepInfo.push("- The logical next step in the conversation");
566
+ stepInfo.push("- Whether conditions for steps are met");
567
+ pc.addInstruction(stepInfo.join("\n"));
568
568
  }
569
569
  }
570
570
  pc.addInteractionHistory(history);