@falai/agent 0.9.0-alpha-1 → 0.9.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 (217) hide show
  1. package/README.md +34 -22
  2. package/dist/cjs/src/core/Agent.d.ts +77 -59
  3. package/dist/cjs/src/core/Agent.d.ts.map +1 -1
  4. package/dist/cjs/src/core/Agent.js +284 -1060
  5. package/dist/cjs/src/core/Agent.js.map +1 -1
  6. package/dist/cjs/src/core/PersistenceManager.d.ts.map +1 -1
  7. package/dist/cjs/src/core/PersistenceManager.js +48 -25
  8. package/dist/cjs/src/core/PersistenceManager.js.map +1 -1
  9. package/dist/cjs/src/core/PromptComposer.d.ts +1 -1
  10. package/dist/cjs/src/core/PromptComposer.d.ts.map +1 -1
  11. package/dist/cjs/src/core/PromptComposer.js.map +1 -1
  12. package/dist/cjs/src/core/ResponseEngine.d.ts +13 -12
  13. package/dist/cjs/src/core/ResponseEngine.d.ts.map +1 -1
  14. package/dist/cjs/src/core/ResponseEngine.js +4 -4
  15. package/dist/cjs/src/core/ResponseEngine.js.map +1 -1
  16. package/dist/cjs/src/core/ResponseModal.d.ts +205 -0
  17. package/dist/cjs/src/core/ResponseModal.d.ts.map +1 -0
  18. package/dist/cjs/src/core/ResponseModal.js +1328 -0
  19. package/dist/cjs/src/core/ResponseModal.js.map +1 -0
  20. package/dist/cjs/src/core/ResponsePipeline.d.ts +66 -38
  21. package/dist/cjs/src/core/ResponsePipeline.d.ts.map +1 -1
  22. package/dist/cjs/src/core/ResponsePipeline.js +72 -4
  23. package/dist/cjs/src/core/ResponsePipeline.js.map +1 -1
  24. package/dist/cjs/src/core/Route.d.ts +24 -5
  25. package/dist/cjs/src/core/Route.d.ts.map +1 -1
  26. package/dist/cjs/src/core/Route.js +45 -1
  27. package/dist/cjs/src/core/Route.js.map +1 -1
  28. package/dist/cjs/src/core/RoutingEngine.d.ts +31 -6
  29. package/dist/cjs/src/core/RoutingEngine.d.ts.map +1 -1
  30. package/dist/cjs/src/core/RoutingEngine.js +113 -9
  31. package/dist/cjs/src/core/RoutingEngine.js.map +1 -1
  32. package/dist/cjs/src/core/SessionManager.d.ts +14 -4
  33. package/dist/cjs/src/core/SessionManager.d.ts.map +1 -1
  34. package/dist/cjs/src/core/SessionManager.js +25 -5
  35. package/dist/cjs/src/core/SessionManager.js.map +1 -1
  36. package/dist/cjs/src/core/Step.d.ts +10 -10
  37. package/dist/cjs/src/core/Step.d.ts.map +1 -1
  38. package/dist/cjs/src/core/Step.js.map +1 -1
  39. package/dist/cjs/src/core/ToolExecutor.d.ts +4 -2
  40. package/dist/cjs/src/core/ToolExecutor.d.ts.map +1 -1
  41. package/dist/cjs/src/core/ToolExecutor.js +13 -3
  42. package/dist/cjs/src/core/ToolExecutor.js.map +1 -1
  43. package/dist/cjs/src/index.d.ts +3 -1
  44. package/dist/cjs/src/index.d.ts.map +1 -1
  45. package/dist/cjs/src/index.js +7 -1
  46. package/dist/cjs/src/index.js.map +1 -1
  47. package/dist/cjs/src/types/agent.d.ts +42 -21
  48. package/dist/cjs/src/types/agent.d.ts.map +1 -1
  49. package/dist/cjs/src/types/agent.js.map +1 -1
  50. package/dist/cjs/src/types/ai.d.ts +1 -1
  51. package/dist/cjs/src/types/ai.d.ts.map +1 -1
  52. package/dist/cjs/src/types/index.d.ts +1 -1
  53. package/dist/cjs/src/types/index.d.ts.map +1 -1
  54. package/dist/cjs/src/types/index.js.map +1 -1
  55. package/dist/cjs/src/types/persistence.d.ts +0 -1
  56. package/dist/cjs/src/types/persistence.d.ts.map +1 -1
  57. package/dist/cjs/src/types/route.d.ts +22 -16
  58. package/dist/cjs/src/types/route.d.ts.map +1 -1
  59. package/dist/cjs/src/types/session.d.ts +6 -11
  60. package/dist/cjs/src/types/session.d.ts.map +1 -1
  61. package/dist/cjs/src/types/tool.d.ts +12 -6
  62. package/dist/cjs/src/types/tool.d.ts.map +1 -1
  63. package/dist/cjs/src/utils/clone.d.ts.map +1 -1
  64. package/dist/cjs/src/utils/clone.js +0 -4
  65. package/dist/cjs/src/utils/clone.js.map +1 -1
  66. package/dist/cjs/src/utils/history.d.ts +30 -1
  67. package/dist/cjs/src/utils/history.d.ts.map +1 -1
  68. package/dist/cjs/src/utils/history.js +169 -23
  69. package/dist/cjs/src/utils/history.js.map +1 -1
  70. package/dist/cjs/src/utils/index.d.ts +1 -1
  71. package/dist/cjs/src/utils/index.d.ts.map +1 -1
  72. package/dist/cjs/src/utils/index.js +5 -1
  73. package/dist/cjs/src/utils/index.js.map +1 -1
  74. package/dist/cjs/src/utils/session.d.ts +2 -2
  75. package/dist/cjs/src/utils/session.d.ts.map +1 -1
  76. package/dist/cjs/src/utils/session.js +6 -26
  77. package/dist/cjs/src/utils/session.js.map +1 -1
  78. package/dist/src/core/Agent.d.ts +77 -59
  79. package/dist/src/core/Agent.d.ts.map +1 -1
  80. package/dist/src/core/Agent.js +285 -1061
  81. package/dist/src/core/Agent.js.map +1 -1
  82. package/dist/src/core/PersistenceManager.d.ts.map +1 -1
  83. package/dist/src/core/PersistenceManager.js +48 -25
  84. package/dist/src/core/PersistenceManager.js.map +1 -1
  85. package/dist/src/core/PromptComposer.d.ts +1 -1
  86. package/dist/src/core/PromptComposer.d.ts.map +1 -1
  87. package/dist/src/core/PromptComposer.js.map +1 -1
  88. package/dist/src/core/ResponseEngine.d.ts +13 -12
  89. package/dist/src/core/ResponseEngine.d.ts.map +1 -1
  90. package/dist/src/core/ResponseEngine.js +4 -4
  91. package/dist/src/core/ResponseEngine.js.map +1 -1
  92. package/dist/src/core/ResponseModal.d.ts +205 -0
  93. package/dist/src/core/ResponseModal.d.ts.map +1 -0
  94. package/dist/src/core/ResponseModal.js +1323 -0
  95. package/dist/src/core/ResponseModal.js.map +1 -0
  96. package/dist/src/core/ResponsePipeline.d.ts +66 -38
  97. package/dist/src/core/ResponsePipeline.d.ts.map +1 -1
  98. package/dist/src/core/ResponsePipeline.js +72 -4
  99. package/dist/src/core/ResponsePipeline.js.map +1 -1
  100. package/dist/src/core/Route.d.ts +24 -5
  101. package/dist/src/core/Route.d.ts.map +1 -1
  102. package/dist/src/core/Route.js +45 -1
  103. package/dist/src/core/Route.js.map +1 -1
  104. package/dist/src/core/RoutingEngine.d.ts +31 -6
  105. package/dist/src/core/RoutingEngine.d.ts.map +1 -1
  106. package/dist/src/core/RoutingEngine.js +113 -9
  107. package/dist/src/core/RoutingEngine.js.map +1 -1
  108. package/dist/src/core/SessionManager.d.ts +14 -4
  109. package/dist/src/core/SessionManager.d.ts.map +1 -1
  110. package/dist/src/core/SessionManager.js +25 -5
  111. package/dist/src/core/SessionManager.js.map +1 -1
  112. package/dist/src/core/Step.d.ts +10 -10
  113. package/dist/src/core/Step.d.ts.map +1 -1
  114. package/dist/src/core/Step.js.map +1 -1
  115. package/dist/src/core/ToolExecutor.d.ts +4 -2
  116. package/dist/src/core/ToolExecutor.d.ts.map +1 -1
  117. package/dist/src/core/ToolExecutor.js +13 -3
  118. package/dist/src/core/ToolExecutor.js.map +1 -1
  119. package/dist/src/index.d.ts +3 -1
  120. package/dist/src/index.d.ts.map +1 -1
  121. package/dist/src/index.js +2 -1
  122. package/dist/src/index.js.map +1 -1
  123. package/dist/src/types/agent.d.ts +42 -21
  124. package/dist/src/types/agent.d.ts.map +1 -1
  125. package/dist/src/types/agent.js.map +1 -1
  126. package/dist/src/types/ai.d.ts +1 -1
  127. package/dist/src/types/ai.d.ts.map +1 -1
  128. package/dist/src/types/index.d.ts +1 -1
  129. package/dist/src/types/index.d.ts.map +1 -1
  130. package/dist/src/types/index.js.map +1 -1
  131. package/dist/src/types/persistence.d.ts +0 -1
  132. package/dist/src/types/persistence.d.ts.map +1 -1
  133. package/dist/src/types/route.d.ts +22 -16
  134. package/dist/src/types/route.d.ts.map +1 -1
  135. package/dist/src/types/session.d.ts +6 -11
  136. package/dist/src/types/session.d.ts.map +1 -1
  137. package/dist/src/types/tool.d.ts +12 -6
  138. package/dist/src/types/tool.d.ts.map +1 -1
  139. package/dist/src/utils/clone.d.ts.map +1 -1
  140. package/dist/src/utils/clone.js +0 -4
  141. package/dist/src/utils/clone.js.map +1 -1
  142. package/dist/src/utils/history.d.ts +30 -1
  143. package/dist/src/utils/history.d.ts.map +1 -1
  144. package/dist/src/utils/history.js +165 -23
  145. package/dist/src/utils/history.js.map +1 -1
  146. package/dist/src/utils/index.d.ts +1 -1
  147. package/dist/src/utils/index.d.ts.map +1 -1
  148. package/dist/src/utils/index.js +1 -1
  149. package/dist/src/utils/index.js.map +1 -1
  150. package/dist/src/utils/session.d.ts +2 -2
  151. package/dist/src/utils/session.d.ts.map +1 -1
  152. package/dist/src/utils/session.js +6 -26
  153. package/dist/src/utils/session.js.map +1 -1
  154. package/docs/README.md +5 -4
  155. package/docs/api/README.md +195 -4
  156. package/docs/api/overview.md +232 -13
  157. package/docs/core/agent/README.md +162 -17
  158. package/docs/core/agent/context-management.md +39 -15
  159. package/docs/core/agent/session-management.md +49 -16
  160. package/docs/core/ai-integration/prompt-composition.md +38 -14
  161. package/docs/core/ai-integration/response-processing.md +28 -17
  162. package/docs/core/conversation-flows/data-collection.md +103 -25
  163. package/docs/core/conversation-flows/route-dsl.md +45 -22
  164. package/docs/core/conversation-flows/routes.md +74 -18
  165. package/docs/core/conversation-flows/step-transitions.md +3 -3
  166. package/docs/core/conversation-flows/steps.md +39 -15
  167. package/docs/core/routing/intelligent-routing.md +18 -9
  168. package/docs/core/tools/tool-definition.md +8 -8
  169. package/docs/core/tools/tool-execution.md +26 -26
  170. package/docs/core/tools/tool-scoping.md +5 -5
  171. package/docs/guides/getting-started/README.md +54 -32
  172. package/docs/guides/migration/README.md +72 -0
  173. package/docs/guides/migration/response-modal-refactor.md +518 -0
  174. package/examples/advanced-patterns/knowledge-based-agent.ts +37 -28
  175. package/examples/advanced-patterns/persistent-onboarding.ts +70 -41
  176. package/examples/advanced-patterns/route-lifecycle-hooks.ts +28 -2
  177. package/examples/advanced-patterns/streaming-responses.ts +197 -119
  178. package/examples/ai-providers/anthropic-integration.ts +40 -33
  179. package/examples/ai-providers/openai-integration.ts +25 -25
  180. package/examples/conversation-flows/completion-transitions.ts +36 -32
  181. package/examples/core-concepts/basic-agent.ts +76 -78
  182. package/examples/core-concepts/modern-streaming-api.ts +309 -0
  183. package/examples/core-concepts/schema-driven-extraction.ts +20 -16
  184. package/examples/core-concepts/session-management.ts +65 -53
  185. package/examples/integrations/database-integration.ts +49 -34
  186. package/examples/integrations/healthcare-integration.ts +96 -91
  187. package/examples/integrations/search-integration.ts +79 -82
  188. package/examples/integrations/server-session-management.ts +25 -17
  189. package/examples/persistence/database-persistence.ts +61 -45
  190. package/examples/persistence/memory-sessions.ts +52 -63
  191. package/examples/persistence/redis-persistence.ts +81 -95
  192. package/examples/tools/basic-tools.ts +73 -62
  193. package/examples/tools/data-enrichment-tools.ts +52 -44
  194. package/package.json +1 -1
  195. package/src/core/Agent.ts +396 -1499
  196. package/src/core/PersistenceManager.ts +51 -27
  197. package/src/core/PromptComposer.ts +1 -1
  198. package/src/core/ResponseEngine.ts +21 -19
  199. package/src/core/ResponseModal.ts +1722 -0
  200. package/src/core/ResponsePipeline.ts +175 -60
  201. package/src/core/Route.ts +58 -6
  202. package/src/core/RoutingEngine.ts +174 -27
  203. package/src/core/SessionManager.ts +32 -8
  204. package/src/core/Step.ts +20 -12
  205. package/src/core/ToolExecutor.ts +19 -5
  206. package/src/index.ts +11 -0
  207. package/src/types/agent.ts +47 -23
  208. package/src/types/ai.ts +1 -1
  209. package/src/types/index.ts +2 -0
  210. package/src/types/persistence.ts +0 -1
  211. package/src/types/route.ts +22 -16
  212. package/src/types/session.ts +6 -12
  213. package/src/types/tool.ts +15 -9
  214. package/src/utils/clone.ts +6 -8
  215. package/src/utils/history.ts +190 -27
  216. package/src/utils/index.ts +4 -0
  217. package/src/utils/session.ts +6 -31
@@ -8,6 +8,7 @@ import type {
8
8
  SessionState,
9
9
  AgentStructuredResponse,
10
10
  Tool,
11
+ RouteTransitionConfig,
11
12
  } from "../types";
12
13
  import { EventKind, MessageRole } from "../types/history";
13
14
  import {
@@ -24,47 +25,51 @@ import { RoutingEngine } from "../core/RoutingEngine";
24
25
  import { ToolExecutor } from "../core/ToolExecutor";
25
26
  import { END_ROUTE_ID } from "../constants";
26
27
 
27
- export interface ResponsePreparationResult<TContext> {
28
+ export interface ResponsePreparationResult<TContext, TData = unknown> {
28
29
  effectiveContext: TContext;
29
- session: SessionState;
30
+ session: SessionState<TData>;
30
31
  }
31
32
 
32
- export interface RoutingResult<TContext> {
33
- selectedRoute: Route<TContext, unknown> | undefined;
34
- selectedStep: Step<TContext, unknown> | undefined;
33
+ export interface RoutingResult<TContext, TData = unknown> {
34
+ selectedRoute: Route<TContext, TData> | undefined;
35
+ selectedStep: Step<TContext, TData> | undefined;
35
36
  responseDirectives: string[] | undefined;
36
- session: SessionState;
37
+ session: SessionState<TData>;
37
38
  isRouteComplete: boolean;
39
+ completedRoutes?: Route<TContext, TData>[];
38
40
  }
39
41
 
40
- export interface ToolExecutionResult {
41
- session: SessionState;
42
+ export interface ToolExecutionResult<TData = unknown> {
43
+ session: SessionState<TData>;
42
44
  toolCalls:
43
45
  | Array<{ toolName: string; arguments: Record<string, unknown> }>
44
46
  | undefined;
45
47
  }
46
48
 
47
- export interface DataCollectionResult {
48
- session: SessionState;
49
- collectedData?: Record<string, unknown>;
49
+ export interface DataCollectionResult<TData = unknown> {
50
+ session: SessionState<TData>;
51
+ collectedData?: Partial<TData>;
50
52
  }
51
53
 
52
54
  /**
53
55
  * Shared response processing logic between respond() and respondStream() methods
54
56
  */
55
- export class ResponsePipeline<TContext = unknown> {
57
+ export class ResponsePipeline<TContext = unknown, TData = unknown> {
56
58
  constructor(
57
- private readonly options: AgentOptions<TContext>,
58
- private readonly routes: Route<TContext, unknown>[],
59
- private readonly tools: Tool<TContext, unknown[], unknown, unknown>[],
60
- private readonly routingEngine: RoutingEngine<TContext>,
59
+ private readonly options: AgentOptions<TContext, TData>,
60
+ private readonly routes: Route<TContext, TData>[],
61
+ private readonly tools: Tool<TContext, TData, unknown[], unknown>[],
62
+ private readonly routingEngine: RoutingEngine<TContext, TData>,
61
63
  private readonly updateContext: (
62
64
  updates: Partial<TContext>
63
65
  ) => Promise<void>,
64
66
  private readonly updateData: (
65
- session: SessionState,
66
- dataUpdate: Partial<unknown>
67
- ) => Promise<SessionState>
67
+ session: SessionState<TData>,
68
+ dataUpdate: Partial<TData>
69
+ ) => Promise<SessionState<TData>>,
70
+ private readonly updateCollectedData?: (
71
+ updates: Partial<TData>
72
+ ) => Promise<void>
68
73
  ) {}
69
74
 
70
75
  /**
@@ -72,8 +77,8 @@ export class ResponsePipeline<TContext = unknown> {
72
77
  */
73
78
  async prepareResponseContext(params: {
74
79
  contextOverride?: Partial<TContext>;
75
- session?: SessionState;
76
- }): Promise<ResponsePreparationResult<TContext>> {
80
+ session?: SessionState<TData>;
81
+ }): Promise<ResponsePreparationResult<TContext, TData>> {
77
82
  const { contextOverride, session } = params;
78
83
 
79
84
  // Get current context (may fetch from provider)
@@ -93,7 +98,7 @@ export class ResponsePipeline<TContext = unknown> {
93
98
  } as TContext;
94
99
 
95
100
  // Initialize or get session (use current session if available)
96
- const targetSession = session || this.currentSession || createSession();
101
+ const targetSession = session || this.currentSession || createSession<TData>();
97
102
 
98
103
  return {
99
104
  effectiveContext,
@@ -105,18 +110,19 @@ export class ResponsePipeline<TContext = unknown> {
105
110
  * Handle routing and step selection logic
106
111
  */
107
112
  async handleRoutingAndStepSelection(params: {
108
- session: SessionState;
113
+ session: SessionState<TData>;
109
114
  history: Event[];
110
115
  context: TContext;
111
116
  signal?: AbortSignal;
112
- }): Promise<RoutingResult<TContext>> {
117
+ }): Promise<RoutingResult<TContext, TData>> {
113
118
  const { session, history, context, signal } = params;
114
119
 
115
120
  // PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use (combined)
116
- let selectedRoute: Route<TContext, unknown> | undefined;
121
+ let selectedRoute: Route<TContext, TData> | undefined;
117
122
  let responseDirectives: string[] | undefined;
118
- let selectedStep: Step<TContext, unknown> | undefined;
123
+ let selectedStep: Step<TContext, TData> | undefined;
119
124
  let isRouteComplete = false;
125
+ let completedRoutes: Route<TContext, TData>[] = [];
120
126
  let targetSession = session;
121
127
 
122
128
  // Check for pending transition from previous route completion
@@ -178,6 +184,7 @@ export class ResponsePipeline<TContext = unknown> {
178
184
  responseDirectives = orchestration.responseDirectives;
179
185
  targetSession = orchestration.session;
180
186
  isRouteComplete = orchestration.isRouteComplete || false;
187
+ completedRoutes = orchestration.completedRoutes || [];
181
188
 
182
189
  // Log if route is complete
183
190
  if (isRouteComplete) {
@@ -193,6 +200,7 @@ export class ResponsePipeline<TContext = unknown> {
193
200
  responseDirectives,
194
201
  session: targetSession,
195
202
  isRouteComplete,
203
+ completedRoutes,
196
204
  };
197
205
  }
198
206
 
@@ -200,18 +208,18 @@ export class ResponsePipeline<TContext = unknown> {
200
208
  * Determine next step and update session
201
209
  */
202
210
  determineNextStep(params: {
203
- selectedRoute: Route<TContext, unknown> | undefined;
204
- selectedStep: Step<TContext, unknown> | undefined;
205
- session: SessionState;
211
+ selectedRoute: Route<TContext, TData> | undefined;
212
+ selectedStep: Step<TContext, TData> | undefined;
213
+ session: SessionState<TData>;
206
214
  isRouteComplete: boolean;
207
- }): { nextStep: Step<TContext, unknown> | undefined; session: SessionState } {
215
+ }): { nextStep: Step<TContext, TData> | undefined; session: SessionState<TData> } {
208
216
  const { selectedRoute, selectedStep, session, isRouteComplete } = params;
209
217
 
210
218
  if (!selectedRoute || isRouteComplete) {
211
219
  return { nextStep: undefined, session };
212
220
  }
213
221
 
214
- let nextStep: Step<TContext, unknown>;
222
+ let nextStep: Step<TContext, TData>;
215
223
 
216
224
  // If we have a selected step from the combined routing decision, use it
217
225
  if (selectedStep) {
@@ -253,12 +261,12 @@ export class ResponsePipeline<TContext = unknown> {
253
261
  */
254
262
  async executeToolCalls(params: {
255
263
  toolCalls: Array<{ toolName: string; arguments: Record<string, unknown> }>;
256
- selectedRoute?: Route<TContext, unknown>;
264
+ selectedRoute?: Route<TContext, TData>;
257
265
  context: TContext;
258
- session: SessionState;
266
+ session: SessionState<TData>;
259
267
  history: Event[];
260
268
  isStreaming?: boolean;
261
- }): Promise<ToolExecutionResult> {
269
+ }): Promise<ToolExecutionResult<TData>> {
262
270
  const {
263
271
  toolCalls,
264
272
  selectedRoute,
@@ -291,11 +299,12 @@ export class ResponsePipeline<TContext = unknown> {
291
299
  continue;
292
300
  }
293
301
 
294
- const toolExecutor = new ToolExecutor<TContext, unknown>();
302
+ const toolExecutor = new ToolExecutor<TContext, TData>();
295
303
  const result = await toolExecutor.executeTool({
296
304
  tool,
297
305
  context,
298
306
  updateContext: this.updateContext,
307
+ updateData: this.updateCollectedData || (async () => {}),
299
308
  history,
300
309
  data: updatedSession.data,
301
310
  });
@@ -311,7 +320,7 @@ export class ResponsePipeline<TContext = unknown> {
311
320
  if (result.dataUpdate) {
312
321
  updatedSession = await this.updateData(
313
322
  updatedSession,
314
- result.dataUpdate
323
+ result.dataUpdate as Partial<TData>
315
324
  );
316
325
  logger.debug(
317
326
  `[ResponseHandler] ${
@@ -341,15 +350,15 @@ export class ResponsePipeline<TContext = unknown> {
341
350
  initialToolCalls:
342
351
  | Array<{ toolName: string; arguments: Record<string, unknown> }>
343
352
  | undefined;
344
- selectedRoute?: Route<TContext, unknown>;
345
- nextStep: Step<TContext, unknown>;
353
+ selectedRoute?: Route<TContext, TData>;
354
+ nextStep: Step<TContext, TData>;
346
355
  responsePrompt: string;
347
356
  history: Event[];
348
357
  context: TContext;
349
- session: SessionState;
358
+ session: SessionState<TData>;
350
359
  responseSchema: Record<string, unknown>;
351
360
  isStreaming?: boolean;
352
- }): Promise<ToolExecutionResult> {
361
+ }): Promise<ToolExecutionResult<TData>> {
353
362
  const {
354
363
  initialToolCalls,
355
364
  selectedRoute,
@@ -469,28 +478,35 @@ export class ResponsePipeline<TContext = unknown> {
469
478
  */
470
479
  async handleDataCollection(params: {
471
480
  structured: AgentStructuredResponse | undefined;
472
- nextStep: Step<TContext, unknown>;
473
- session: SessionState;
474
- }): Promise<DataCollectionResult> {
481
+ nextStep: Step<TContext, TData>;
482
+ session: SessionState<TData>;
483
+ }): Promise<DataCollectionResult<TData>> {
475
484
  const { structured, nextStep, session } = params;
476
485
 
477
486
  if (!structured || !nextStep.collect) {
478
487
  return { session };
479
488
  }
480
489
 
481
- const collectedData: Record<string, unknown> = {};
490
+ const collectedData: Partial<TData> = {};
482
491
  // The structured response includes both base fields and collected extraction fields
483
492
  const structuredData = structured as AgentStructuredResponse &
484
493
  Record<string, unknown>;
485
494
 
486
495
  for (const field of nextStep.collect) {
487
- if (field in structuredData) {
488
- collectedData[field] = structuredData[field];
496
+ const fieldKey = field as string;
497
+ if (fieldKey in structuredData) {
498
+ (collectedData as Record<string, unknown>)[fieldKey] = structuredData[fieldKey];
489
499
  }
490
500
  }
491
501
 
492
502
  let updatedSession = session;
493
503
  if (Object.keys(collectedData).length > 0) {
504
+ // Update agent-level collected data with validation if available
505
+ if (this.updateCollectedData) {
506
+ await this.updateCollectedData(collectedData);
507
+ }
508
+
509
+ // Update session with validated data
494
510
  updatedSession = await this.updateData(session, collectedData);
495
511
  logger.debug(`[ResponseHandler] Collected data:`, collectedData);
496
512
  }
@@ -523,11 +539,11 @@ export class ResponsePipeline<TContext = unknown> {
523
539
  * Handle route completion logic
524
540
  */
525
541
  async handleRouteCompletion(params: {
526
- selectedRoute: Route<TContext, unknown>;
527
- session: SessionState;
542
+ selectedRoute: Route<TContext, TData>;
543
+ session: SessionState<TData>;
528
544
  context: TContext;
529
545
  history: Event[];
530
- }): Promise<{ session: SessionState; hasTransition: boolean }> {
546
+ }): Promise<{ session: SessionState<TData>; hasTransition: boolean }> {
531
547
  const { selectedRoute, session, context, history } = params;
532
548
 
533
549
  // Check for onComplete transition
@@ -590,8 +606,8 @@ export class ResponsePipeline<TContext = unknown> {
590
606
  */
591
607
  private findAvailableTool(
592
608
  toolName: string,
593
- route?: Route<TContext, unknown>
594
- ): Tool<TContext, unknown[], unknown, unknown> | undefined {
609
+ route?: Route<TContext, TData>
610
+ ): Tool<TContext, TData, unknown[], unknown> | undefined {
595
611
  // Check route-level tools first (if route provided)
596
612
  if (route) {
597
613
  const routeTool = route
@@ -610,12 +626,12 @@ export class ResponsePipeline<TContext = unknown> {
610
626
  * Collect all available tools for the given route and step context
611
627
  */
612
628
  private collectAvailableTools(
613
- route?: Route<TContext, unknown>,
614
- step?: Step<TContext, unknown>
629
+ route?: Route<TContext, TData>,
630
+ step?: Step<TContext, TData>
615
631
  ): Array<{ id: string; description?: string; parameters?: unknown }> {
616
632
  const availableTools = new Map<
617
633
  string,
618
- Tool<TContext, unknown[], unknown, unknown>
634
+ Tool<TContext, TData, unknown[], unknown>
619
635
  >();
620
636
 
621
637
  // Add agent-level tools
@@ -633,7 +649,7 @@ export class ResponsePipeline<TContext = unknown> {
633
649
  // Filter by step-level allowed tools if specified
634
650
  if (step?.tools) {
635
651
  const allowedToolIds = new Set<string>();
636
- const stepTools: Tool<TContext, unknown[], unknown, unknown>[] = [];
652
+ const stepTools: Tool<TContext, TData, unknown[], unknown>[] = [];
637
653
 
638
654
  for (const toolRef of step.tools) {
639
655
  if (typeof toolRef === "string") {
@@ -652,9 +668,9 @@ export class ResponsePipeline<TContext = unknown> {
652
668
  if (allowedToolIds.size > 0) {
653
669
  const filteredTools = new Map<
654
670
  string,
655
- Tool<TContext, unknown[], unknown, unknown>
671
+ Tool<TContext, TData, unknown[], unknown>
656
672
  >();
657
- for (const toolId of allowedToolIds) {
673
+ for (const toolId of Array.from(allowedToolIds)) {
658
674
  const tool = availableTools.get(toolId);
659
675
  if (tool) {
660
676
  filteredTools.set(toolId, tool);
@@ -694,14 +710,14 @@ export class ResponsePipeline<TContext = unknown> {
694
710
 
695
711
  // These need to be passed in or accessed differently since ResponseHandler is not part of Agent
696
712
  private context?: TContext;
697
- private currentSession?: SessionState;
713
+ private currentSession?: SessionState<TData>;
698
714
 
699
715
  // Setters for context and current session (needed for beforeRespond hook)
700
716
  setContext(context: TContext | undefined): void {
701
717
  this.context = context;
702
718
  }
703
719
 
704
- setCurrentSession(session: SessionState | undefined): void {
720
+ setCurrentSession(session: SessionState<TData> | undefined): void {
705
721
  this.currentSession = session;
706
722
  }
707
723
 
@@ -709,7 +725,106 @@ export class ResponsePipeline<TContext = unknown> {
709
725
  return this.context;
710
726
  }
711
727
 
712
- public getCurrentSession(): SessionState | undefined {
728
+ public getCurrentSession(): SessionState<TData> | undefined {
713
729
  return this.currentSession;
714
730
  }
731
+
732
+ /**
733
+ * Handle cross-route completion evaluation and notifications
734
+ * This method evaluates all routes for completion and can trigger completion handlers
735
+ */
736
+ async handleCrossRouteCompletion(params: {
737
+ routes: Route<TContext, TData>[];
738
+ session: SessionState<TData>;
739
+ context: TContext;
740
+ history: Event[];
741
+ }): Promise<{
742
+ session: SessionState<TData>;
743
+ completedRoutes: Route<TContext, TData>[];
744
+ pendingTransitions: Array<{
745
+ route: Route<TContext, TData>;
746
+ transitionConfig: RouteTransitionConfig<TContext, TData>;
747
+ }>;
748
+ }> {
749
+ const { routes, session, context } = params;
750
+
751
+ // Evaluate all routes for completion
752
+ const completedRoutes: Route<TContext, TData>[] = [];
753
+ const pendingTransitions: Array<{
754
+ route: Route<TContext, TData>;
755
+ transitionConfig: RouteTransitionConfig<TContext, TData>;
756
+ }> = [];
757
+
758
+ for (const route of routes) {
759
+ if (route.isComplete(session.data || {})) {
760
+ completedRoutes.push(route);
761
+
762
+ // Check for onComplete transitions
763
+ const transitionConfig = await route.evaluateOnComplete(
764
+ { data: session.data },
765
+ context
766
+ );
767
+
768
+ if (transitionConfig) {
769
+ pendingTransitions.push({ route, transitionConfig });
770
+ }
771
+
772
+ logger.debug(
773
+ `[ResponsePipeline] Route completed: ${route.title} ` +
774
+ `(${Math.round(route.getCompletionProgress(session.data || {}) * 100)}%)`
775
+ );
776
+ }
777
+ }
778
+
779
+ // Log completion status for all routes
780
+ if (completedRoutes.length > 0) {
781
+ logger.debug(
782
+ `[ResponsePipeline] Cross-route completion evaluation: ` +
783
+ `${completedRoutes.length}/${routes.length} routes complete`
784
+ );
785
+ }
786
+
787
+ return {
788
+ session,
789
+ completedRoutes,
790
+ pendingTransitions,
791
+ };
792
+ }
793
+
794
+ /**
795
+ * Update data flow to ensure agent-level data consistency
796
+ * This method ensures that data updates are properly validated and propagated
797
+ */
798
+ async updateDataFlow(params: {
799
+ session: SessionState<TData>;
800
+ dataUpdate: Partial<TData>;
801
+ routes: Route<TContext, TData>[];
802
+ }): Promise<SessionState<TData>> {
803
+ const { session, dataUpdate, routes } = params;
804
+
805
+ // Update session data
806
+ const updatedSession = await this.updateData(session, dataUpdate);
807
+
808
+ // Update agent-level data if handler is available
809
+ if (this.updateCollectedData) {
810
+ await this.updateCollectedData(dataUpdate);
811
+ }
812
+
813
+ // Evaluate route completions after data update
814
+ const completionResults = await this.handleCrossRouteCompletion({
815
+ routes,
816
+ session: updatedSession,
817
+ context: this.context!,
818
+ history: [],
819
+ });
820
+
821
+ // Log any newly completed routes
822
+ if (completionResults.completedRoutes.length > 0) {
823
+ logger.debug(
824
+ `[ResponsePipeline] Data update resulted in ${completionResults.completedRoutes.length} completed routes`
825
+ );
826
+ }
827
+
828
+ return completionResults.session;
829
+ }
715
830
  }
package/src/core/Route.ts CHANGED
@@ -39,8 +39,9 @@ export class Route<TContext = unknown, TData = unknown> {
39
39
  "step" | "condition" | "skipIf"
40
40
  >;
41
41
  public readonly responseOutputSchema?: StructuredSchema;
42
- public readonly schema?: StructuredSchema;
43
42
  public readonly initialData?: Partial<TData>;
43
+ public readonly requiredFields?: (keyof TData)[];
44
+ public readonly optionalFields?: (keyof TData)[];
44
45
  public readonly onComplete?:
45
46
  | string
46
47
  | RouteTransitionConfig<TContext, TData>
@@ -49,7 +50,7 @@ export class Route<TContext = unknown, TData = unknown> {
49
50
  public routingExtrasSchema?: StructuredSchema;
50
51
  public guidelines: Guideline<TContext>[] = [];
51
52
  public terms: Term<TContext>[] = [];
52
- public tools: Tool<TContext, unknown[], unknown, TData>[] = [];
53
+ public tools: Tool<TContext, TData, unknown[], unknown>[] = [];
53
54
  public knowledgeBase: Record<string, unknown> = {};
54
55
 
55
56
  constructor(options: RouteOptions<TContext, TData>) {
@@ -87,8 +88,9 @@ export class Route<TContext = unknown, TData = unknown> {
87
88
  };
88
89
  this.routingExtrasSchema = options.routingExtrasSchema;
89
90
  this.responseOutputSchema = options.responseOutputSchema;
90
- this.schema = options.schema;
91
91
  this.initialData = options.initialData;
92
+ this.requiredFields = options.requiredFields;
93
+ this.optionalFields = options.optionalFields;
92
94
  this.onComplete = options.onComplete;
93
95
  this.hooks = options.hooks;
94
96
 
@@ -166,7 +168,7 @@ export class Route<TContext = unknown, TData = unknown> {
166
168
  /**
167
169
  * Register a tool for this route
168
170
  */
169
- createTool(tool: Tool<TContext, unknown[], unknown, TData>): this {
171
+ createTool(tool: Tool<TContext, TData, unknown[], unknown>): this {
170
172
  this.tools.push(tool);
171
173
  return this;
172
174
  }
@@ -174,7 +176,7 @@ export class Route<TContext = unknown, TData = unknown> {
174
176
  /**
175
177
  * Register multiple tools for this route
176
178
  */
177
- registerTools(tools: Tool<TContext, unknown[], unknown, TData>[]): this {
179
+ registerTools(tools: Tool<TContext, TData, unknown[], unknown>[]): this {
178
180
  tools.forEach((tool) => this.createTool(tool));
179
181
  return this;
180
182
  }
@@ -196,7 +198,7 @@ export class Route<TContext = unknown, TData = unknown> {
196
198
  /**
197
199
  * Get all tools for this route
198
200
  */
199
- getTools(): Tool<TContext, unknown[], unknown, TData>[] {
201
+ getTools(): Tool<TContext, TData, unknown[], unknown>[] {
200
202
  return [...this.tools];
201
203
  }
202
204
 
@@ -353,6 +355,56 @@ export class Route<TContext = unknown, TData = unknown> {
353
355
  }
354
356
  }
355
357
 
358
+ /**
359
+ * Check if this route is complete based on the provided data
360
+ * @param data - Currently collected agent-level data
361
+ * @returns true if all required fields are present, false otherwise
362
+ */
363
+ isComplete(data: Partial<TData>): boolean {
364
+ if (!this.requiredFields || this.requiredFields.length === 0) {
365
+ return true; // No required fields means route is always complete
366
+ }
367
+
368
+ return this.requiredFields.every(field => {
369
+ const value = data[field];
370
+ return value !== undefined && value !== null && value !== '';
371
+ });
372
+ }
373
+
374
+ /**
375
+ * Get the list of missing required fields for this route
376
+ * @param data - Currently collected agent-level data
377
+ * @returns Array of missing required field keys
378
+ */
379
+ getMissingRequiredFields(data: Partial<TData>): (keyof TData)[] {
380
+ if (!this.requiredFields || this.requiredFields.length === 0) {
381
+ return [];
382
+ }
383
+
384
+ return this.requiredFields.filter(field => {
385
+ const value = data[field];
386
+ return value === undefined || value === null || value === '';
387
+ });
388
+ }
389
+
390
+ /**
391
+ * Get the completion progress for this route as a percentage
392
+ * @param data - Currently collected agent-level data
393
+ * @returns Completion progress as a number between 0 and 1
394
+ */
395
+ getCompletionProgress(data: Partial<TData>): number {
396
+ if (!this.requiredFields || this.requiredFields.length === 0) {
397
+ return 1; // No required fields means 100% complete
398
+ }
399
+
400
+ const completedFields = this.requiredFields.filter(field => {
401
+ const value = data[field];
402
+ return value !== undefined && value !== null && value !== '';
403
+ });
404
+
405
+ return completedFields.length / this.requiredFields.length;
406
+ }
407
+
356
408
  /**
357
409
  * Evaluate the onComplete handler and return transition config
358
410
  * @param session - Current session step