@falai/agent 0.9.0-alpha-2 → 0.9.2

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 (179) hide show
  1. package/README.md +42 -34
  2. package/dist/cjs/src/core/Agent.d.ts +48 -44
  3. package/dist/cjs/src/core/Agent.d.ts.map +1 -1
  4. package/dist/cjs/src/core/Agent.js +151 -1110
  5. package/dist/cjs/src/core/Agent.js.map +1 -1
  6. package/dist/cjs/src/core/ResponseModal.d.ts +211 -0
  7. package/dist/cjs/src/core/ResponseModal.d.ts.map +1 -0
  8. package/dist/cjs/src/core/ResponseModal.js +1394 -0
  9. package/dist/cjs/src/core/ResponseModal.js.map +1 -0
  10. package/dist/cjs/src/core/ResponsePipeline.d.ts +8 -4
  11. package/dist/cjs/src/core/ResponsePipeline.d.ts.map +1 -1
  12. package/dist/cjs/src/core/ResponsePipeline.js +48 -20
  13. package/dist/cjs/src/core/ResponsePipeline.js.map +1 -1
  14. package/dist/cjs/src/core/Route.d.ts +12 -5
  15. package/dist/cjs/src/core/Route.d.ts.map +1 -1
  16. package/dist/cjs/src/core/Route.js +26 -5
  17. package/dist/cjs/src/core/Route.js.map +1 -1
  18. package/dist/cjs/src/core/RoutingEngine.d.ts +5 -0
  19. package/dist/cjs/src/core/RoutingEngine.d.ts.map +1 -1
  20. package/dist/cjs/src/core/RoutingEngine.js +37 -25
  21. package/dist/cjs/src/core/RoutingEngine.js.map +1 -1
  22. package/dist/cjs/src/core/SessionManager.d.ts +9 -1
  23. package/dist/cjs/src/core/SessionManager.d.ts.map +1 -1
  24. package/dist/cjs/src/core/SessionManager.js +27 -5
  25. package/dist/cjs/src/core/SessionManager.js.map +1 -1
  26. package/dist/cjs/src/core/Step.d.ts +60 -7
  27. package/dist/cjs/src/core/Step.d.ts.map +1 -1
  28. package/dist/cjs/src/core/Step.js +151 -4
  29. package/dist/cjs/src/core/Step.js.map +1 -1
  30. package/dist/cjs/src/core/ToolManager.d.ts +234 -0
  31. package/dist/cjs/src/core/ToolManager.d.ts.map +1 -0
  32. package/dist/cjs/src/core/ToolManager.js +1117 -0
  33. package/dist/cjs/src/core/ToolManager.js.map +1 -0
  34. package/dist/cjs/src/index.d.ts +5 -4
  35. package/dist/cjs/src/index.d.ts.map +1 -1
  36. package/dist/cjs/src/index.js +11 -3
  37. package/dist/cjs/src/index.js.map +1 -1
  38. package/dist/cjs/src/types/agent.d.ts +2 -1
  39. package/dist/cjs/src/types/agent.d.ts.map +1 -1
  40. package/dist/cjs/src/types/ai.d.ts +1 -1
  41. package/dist/cjs/src/types/ai.d.ts.map +1 -1
  42. package/dist/cjs/src/types/index.d.ts +3 -2
  43. package/dist/cjs/src/types/index.d.ts.map +1 -1
  44. package/dist/cjs/src/types/index.js +3 -1
  45. package/dist/cjs/src/types/index.js.map +1 -1
  46. package/dist/cjs/src/types/route.d.ts +6 -4
  47. package/dist/cjs/src/types/route.d.ts.map +1 -1
  48. package/dist/cjs/src/types/tool.d.ts +84 -14
  49. package/dist/cjs/src/types/tool.d.ts.map +1 -1
  50. package/dist/cjs/src/types/tool.js +13 -0
  51. package/dist/cjs/src/types/tool.js.map +1 -1
  52. package/dist/cjs/src/utils/clone.d.ts.map +1 -1
  53. package/dist/cjs/src/utils/clone.js +0 -4
  54. package/dist/cjs/src/utils/clone.js.map +1 -1
  55. package/dist/cjs/src/utils/history.d.ts +30 -1
  56. package/dist/cjs/src/utils/history.d.ts.map +1 -1
  57. package/dist/cjs/src/utils/history.js +169 -23
  58. package/dist/cjs/src/utils/history.js.map +1 -1
  59. package/dist/cjs/src/utils/index.d.ts +1 -1
  60. package/dist/cjs/src/utils/index.d.ts.map +1 -1
  61. package/dist/cjs/src/utils/index.js +5 -1
  62. package/dist/cjs/src/utils/index.js.map +1 -1
  63. package/dist/src/core/Agent.d.ts +48 -44
  64. package/dist/src/core/Agent.d.ts.map +1 -1
  65. package/dist/src/core/Agent.js +152 -1111
  66. package/dist/src/core/Agent.js.map +1 -1
  67. package/dist/src/core/ResponseModal.d.ts +211 -0
  68. package/dist/src/core/ResponseModal.d.ts.map +1 -0
  69. package/dist/src/core/ResponseModal.js +1389 -0
  70. package/dist/src/core/ResponseModal.js.map +1 -0
  71. package/dist/src/core/ResponsePipeline.d.ts +8 -4
  72. package/dist/src/core/ResponsePipeline.d.ts.map +1 -1
  73. package/dist/src/core/ResponsePipeline.js +48 -20
  74. package/dist/src/core/ResponsePipeline.js.map +1 -1
  75. package/dist/src/core/Route.d.ts +12 -5
  76. package/dist/src/core/Route.d.ts.map +1 -1
  77. package/dist/src/core/Route.js +26 -5
  78. package/dist/src/core/Route.js.map +1 -1
  79. package/dist/src/core/RoutingEngine.d.ts +5 -0
  80. package/dist/src/core/RoutingEngine.d.ts.map +1 -1
  81. package/dist/src/core/RoutingEngine.js +37 -25
  82. package/dist/src/core/RoutingEngine.js.map +1 -1
  83. package/dist/src/core/SessionManager.d.ts +9 -1
  84. package/dist/src/core/SessionManager.d.ts.map +1 -1
  85. package/dist/src/core/SessionManager.js +27 -5
  86. package/dist/src/core/SessionManager.js.map +1 -1
  87. package/dist/src/core/Step.d.ts +60 -7
  88. package/dist/src/core/Step.d.ts.map +1 -1
  89. package/dist/src/core/Step.js +151 -4
  90. package/dist/src/core/Step.js.map +1 -1
  91. package/dist/src/core/ToolManager.d.ts +234 -0
  92. package/dist/src/core/ToolManager.d.ts.map +1 -0
  93. package/dist/src/core/ToolManager.js +1111 -0
  94. package/dist/src/core/ToolManager.js.map +1 -0
  95. package/dist/src/index.d.ts +5 -4
  96. package/dist/src/index.d.ts.map +1 -1
  97. package/dist/src/index.js +3 -2
  98. package/dist/src/index.js.map +1 -1
  99. package/dist/src/types/agent.d.ts +2 -1
  100. package/dist/src/types/agent.d.ts.map +1 -1
  101. package/dist/src/types/ai.d.ts +1 -1
  102. package/dist/src/types/ai.d.ts.map +1 -1
  103. package/dist/src/types/index.d.ts +3 -2
  104. package/dist/src/types/index.d.ts.map +1 -1
  105. package/dist/src/types/index.js +1 -0
  106. package/dist/src/types/index.js.map +1 -1
  107. package/dist/src/types/route.d.ts +6 -4
  108. package/dist/src/types/route.d.ts.map +1 -1
  109. package/dist/src/types/tool.d.ts +84 -14
  110. package/dist/src/types/tool.d.ts.map +1 -1
  111. package/dist/src/types/tool.js +12 -1
  112. package/dist/src/types/tool.js.map +1 -1
  113. package/dist/src/utils/clone.d.ts.map +1 -1
  114. package/dist/src/utils/clone.js +0 -4
  115. package/dist/src/utils/clone.js.map +1 -1
  116. package/dist/src/utils/history.d.ts +30 -1
  117. package/dist/src/utils/history.d.ts.map +1 -1
  118. package/dist/src/utils/history.js +165 -23
  119. package/dist/src/utils/history.js.map +1 -1
  120. package/dist/src/utils/index.d.ts +1 -1
  121. package/dist/src/utils/index.d.ts.map +1 -1
  122. package/dist/src/utils/index.js +1 -1
  123. package/dist/src/utils/index.js.map +1 -1
  124. package/docs/CONTRIBUTING.md +40 -0
  125. package/docs/README.md +14 -6
  126. package/docs/api/README.md +235 -45
  127. package/docs/api/overview.md +140 -33
  128. package/docs/core/agent/session-management.md +152 -5
  129. package/docs/core/ai-integration/response-processing.md +115 -4
  130. package/docs/core/conversation-flows/routes.md +130 -0
  131. package/docs/core/error-handling.md +638 -0
  132. package/docs/core/tools/tool-definition.md +684 -60
  133. package/docs/core/tools/tool-scoping.md +244 -53
  134. package/docs/guides/error-handling-patterns.md +578 -0
  135. package/docs/guides/getting-started/README.md +139 -28
  136. package/docs/guides/migration/README.md +72 -0
  137. package/docs/guides/migration/response-modal-refactor.md +518 -0
  138. package/examples/advanced-patterns/knowledge-based-agent.ts +6 -6
  139. package/examples/advanced-patterns/persistent-onboarding.ts +30 -43
  140. package/examples/advanced-patterns/streaming-responses.ts +169 -96
  141. package/examples/ai-providers/anthropic-integration.ts +9 -5
  142. package/examples/ai-providers/openai-integration.ts +11 -7
  143. package/examples/core-concepts/basic-agent.ts +106 -67
  144. package/examples/core-concepts/modern-streaming-api.ts +309 -0
  145. package/examples/core-concepts/schema-driven-extraction.ts +10 -7
  146. package/examples/core-concepts/session-management.ts +71 -18
  147. package/examples/integrations/healthcare-integration.ts +15 -29
  148. package/examples/integrations/server-session-management.ts +3 -3
  149. package/examples/persistence/memory-sessions.ts +3 -3
  150. package/examples/tools/basic-tools.ts +293 -89
  151. package/examples/tools/data-enrichment-tools.ts +185 -75
  152. package/package.json +1 -1
  153. package/src/core/Agent.ts +190 -1529
  154. package/src/core/ResponseModal.ts +1798 -0
  155. package/src/core/ResponsePipeline.ts +83 -57
  156. package/src/core/Route.ts +39 -12
  157. package/src/core/RoutingEngine.ts +46 -42
  158. package/src/core/SessionManager.ts +39 -7
  159. package/src/core/Step.ts +198 -20
  160. package/src/core/ToolManager.ts +1394 -0
  161. package/src/index.ts +19 -3
  162. package/src/types/agent.ts +2 -1
  163. package/src/types/ai.ts +1 -1
  164. package/src/types/index.ts +13 -2
  165. package/src/types/route.ts +6 -4
  166. package/src/types/tool.ts +116 -25
  167. package/src/utils/clone.ts +6 -8
  168. package/src/utils/history.ts +190 -27
  169. package/src/utils/index.ts +4 -0
  170. package/dist/cjs/src/core/ToolExecutor.d.ts +0 -45
  171. package/dist/cjs/src/core/ToolExecutor.d.ts.map +0 -1
  172. package/dist/cjs/src/core/ToolExecutor.js +0 -84
  173. package/dist/cjs/src/core/ToolExecutor.js.map +0 -1
  174. package/dist/src/core/ToolExecutor.d.ts +0 -45
  175. package/dist/src/core/ToolExecutor.d.ts.map +0 -1
  176. package/dist/src/core/ToolExecutor.js +0 -80
  177. package/dist/src/core/ToolExecutor.js.map +0 -1
  178. package/docs/core/tools/tool-execution.md +0 -815
  179. package/src/core/ToolExecutor.ts +0 -126
@@ -0,0 +1,1394 @@
1
+ "use strict";
2
+ /**
3
+ * ResponseModal handles all response generation logic for the Agent
4
+ * Provides both streaming and non-streaming response generation with unified logic
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ResponseModal = exports.ResponseGenerationError = void 0;
8
+ const types_1 = require("../types");
9
+ const Step_1 = require("./Step");
10
+ const ResponseEngine_1 = require("./ResponseEngine");
11
+ const ResponsePipeline_1 = require("./ResponsePipeline");
12
+ const utils_1 = require("../utils");
13
+ const constants_1 = require("../constants");
14
+ /**
15
+ * Error class for response generation failures
16
+ */
17
+ class ResponseGenerationError extends Error {
18
+ constructor(message, details) {
19
+ super(message);
20
+ this.details = details;
21
+ this.name = 'ResponseGenerationError';
22
+ // Preserve stack trace from original error if available
23
+ if (details?.originalError instanceof Error && details.originalError.stack) {
24
+ this.stack = `${this.stack}\nCaused by: ${details.originalError.stack}`;
25
+ }
26
+ }
27
+ /**
28
+ * Create a ResponseGenerationError from an unknown error
29
+ */
30
+ static fromError(error, phase, params, context) {
31
+ const message = error instanceof Error ? error.message : String(error);
32
+ return new ResponseGenerationError(`Response generation failed in ${phase}: ${message}`, { originalError: error, params, phase, context });
33
+ }
34
+ /**
35
+ * Check if an error is a ResponseGenerationError
36
+ */
37
+ static isResponseGenerationError(error) {
38
+ return error instanceof ResponseGenerationError;
39
+ }
40
+ }
41
+ exports.ResponseGenerationError = ResponseGenerationError;
42
+ /**
43
+ * ResponseModal class that encapsulates all response generation logic
44
+ * Uses unified approach for both streaming and non-streaming responses
45
+ */
46
+ class ResponseModal {
47
+ constructor(agent, options) {
48
+ this.agent = agent;
49
+ this.options = options;
50
+ // Initialize response engine
51
+ this.responseEngine = new ResponseEngine_1.ResponseEngine();
52
+ // Initialize response pipeline with agent dependencies
53
+ this.responsePipeline = new ResponsePipeline_1.ResponsePipeline(this.agent.getAgentOptions(), () => this.agent.getRoutes(), // Pass a function to get routes dynamically
54
+ this.agent.getTools(), this.agent.getRoutingEngine(), this.agent.updateContext.bind(this.agent), this.agent.getUpdateDataMethod(), this.agent.updateCollectedData.bind(this.agent), this.getToolManager());
55
+ }
56
+ /**
57
+ * Generate a non-streaming response using unified logic
58
+ */
59
+ async respond(params) {
60
+ try {
61
+ // Use unified response preparation and routing
62
+ const responseContext = await this.prepareUnifiedResponseContext(params);
63
+ // Generate response using unified logic
64
+ const result = await this.generateUnifiedResponse(responseContext);
65
+ // Finalize session
66
+ await this.finalizeSession(result.session, responseContext.effectiveContext);
67
+ return result;
68
+ }
69
+ catch (error) {
70
+ throw new ResponseGenerationError(`Failed to generate response: ${error instanceof Error ? error.message : String(error)}`, { originalError: error, params, phase: 'response_generation' });
71
+ }
72
+ }
73
+ /**
74
+ * Generate a streaming response using unified logic
75
+ */
76
+ async *respondStream(params) {
77
+ try {
78
+ // Use unified response preparation and routing
79
+ const responseContext = await this.prepareUnifiedResponseContext(params);
80
+ // Generate streaming response using unified logic
81
+ yield* this.generateUnifiedStreamingResponse(responseContext);
82
+ }
83
+ catch (error) {
84
+ // Stream error to caller
85
+ yield {
86
+ delta: "",
87
+ accumulated: "",
88
+ done: true,
89
+ session: params.session || await this.agent.session.getOrCreate(),
90
+ error: new ResponseGenerationError(`Streaming response failed: ${error instanceof Error ? error.message : String(error)}`, { originalError: error, params, phase: 'streaming' }),
91
+ };
92
+ }
93
+ }
94
+ /**
95
+ * Modern streaming API - simple interface like chat()
96
+ */
97
+ async *stream(message, options) {
98
+ // Determine which history to use
99
+ let history;
100
+ if (options?.history) {
101
+ // Use provided history for this response only
102
+ history = options.history;
103
+ }
104
+ else {
105
+ // Add user message to session history if provided
106
+ if (message) {
107
+ await this.agent.session.addMessage("user", message);
108
+ }
109
+ history = this.agent.session.getHistory();
110
+ }
111
+ // Get or create session
112
+ let session = await this.agent.session.getOrCreate();
113
+ // Merge agent's collected data into session (agent data takes precedence)
114
+ const collectedData = this.agent.getCollectedData();
115
+ if (Object.keys(collectedData).length > 0) {
116
+ session = (0, utils_1.mergeCollected)(session, collectedData);
117
+ // Update the session manager with the merged data
118
+ await this.agent.session.setData(collectedData);
119
+ utils_1.logger.debug("[ResponseModal] Merged agent collected data into stream session:", collectedData);
120
+ }
121
+ // Stream response using existing respondStream method
122
+ let finalMessage = "";
123
+ for await (const chunk of this.respondStream({
124
+ history,
125
+ session,
126
+ contextOverride: options?.contextOverride,
127
+ signal: options?.signal,
128
+ })) {
129
+ // Accumulate the final message for session history
130
+ if (chunk.done) {
131
+ finalMessage = chunk.accumulated;
132
+ }
133
+ yield chunk;
134
+ }
135
+ // Add agent response to session history (only if not using override history)
136
+ if (!options?.history && finalMessage) {
137
+ await this.agent.session.addMessage("assistant", finalMessage);
138
+ }
139
+ }
140
+ /**
141
+ * Modern non-streaming API - equivalent to chat() but more explicit
142
+ */
143
+ async generate(message, options) {
144
+ // Determine which history to use
145
+ let history;
146
+ if (options?.history) {
147
+ // Use provided history for this response only
148
+ history = options.history;
149
+ }
150
+ else {
151
+ // Add user message to session history if provided
152
+ if (message) {
153
+ await this.agent.session.addMessage("user", message);
154
+ }
155
+ history = this.agent.session.getHistory();
156
+ }
157
+ // Get or create session
158
+ let session = await this.agent.session.getOrCreate();
159
+ // Merge agent's collected data into session (agent data takes precedence)
160
+ const collectedData = this.agent.getCollectedData();
161
+ if (Object.keys(collectedData).length > 0) {
162
+ session = (0, utils_1.mergeCollected)(session, collectedData);
163
+ // Update the session manager with the merged data
164
+ await this.agent.session.setData(collectedData);
165
+ utils_1.logger.debug("[ResponseModal] Merged agent collected data into generate session:", collectedData);
166
+ }
167
+ // Generate response using existing respond method
168
+ const result = await this.respond({
169
+ history,
170
+ session,
171
+ contextOverride: options?.contextOverride,
172
+ signal: options?.signal,
173
+ });
174
+ // Add agent response to session history (only if not using override history)
175
+ if (!options?.history) {
176
+ await this.agent.session.addMessage("assistant", result.message);
177
+ }
178
+ // Ensure the result includes the current session
179
+ return {
180
+ ...result,
181
+ session: result.session || this.agent.session.current,
182
+ };
183
+ }
184
+ /**
185
+ * Get the response engine instance
186
+ * @internal
187
+ */
188
+ getResponseEngine() {
189
+ return this.responseEngine;
190
+ }
191
+ /**
192
+ * Get the response pipeline instance
193
+ * @internal
194
+ */
195
+ getResponsePipeline() {
196
+ return this.responsePipeline;
197
+ }
198
+ /**
199
+ * Get the ToolManager instance from the agent
200
+ * @private
201
+ */
202
+ getToolManager() {
203
+ // Check if agent has a tool property (ToolManager)
204
+ if (this.agent && 'tool' in this.agent && this.agent.tool) {
205
+ return this.agent.tool;
206
+ }
207
+ // Log warning if ToolManager is not available
208
+ utils_1.logger.warn(`[ResponseModal] ToolManager not available on agent - tool execution will use fallback methods`);
209
+ return undefined;
210
+ }
211
+ // UNIFIED RESPONSE LOGIC - Consolidates common logic between streaming and non-streaming
212
+ // ============================================================================
213
+ /**
214
+ * Unified response preparation - handles context setup, session management, and routing
215
+ * This consolidates common logic between streaming and non-streaming responses
216
+ * @private
217
+ */
218
+ async prepareUnifiedResponseContext(params) {
219
+ try {
220
+ const { history: simpleHistory, contextOverride, signal } = params;
221
+ // Validate input parameters
222
+ if (!simpleHistory) {
223
+ throw new ResponseGenerationError('History is required for response generation', { params, phase: 'validation' });
224
+ }
225
+ // Convert HistoryItem[] to Event[] for internal processing
226
+ const historyEvents = (0, utils_1.historyToEvents)(simpleHistory);
227
+ // Keep original HistoryItem[] format for external APIs
228
+ const history = simpleHistory;
229
+ // Use ResponsePipeline for optimized context and session preparation
230
+ // This leverages existing optimizations and avoids code duplication
231
+ let responseContext;
232
+ try {
233
+ // Set current context and session in pipeline for consistency
234
+ this.responsePipeline.setContext(await this.agent.getContext());
235
+ this.responsePipeline.setCurrentSession(this.agent.getCurrentSession());
236
+ responseContext = await this.responsePipeline.prepareResponseContext({
237
+ contextOverride,
238
+ session: params.session ? (0, utils_1.cloneDeep)(params.session) : undefined,
239
+ });
240
+ }
241
+ catch (error) {
242
+ throw ResponseGenerationError.fromError(error, 'pipeline_context_preparation', params);
243
+ }
244
+ const { effectiveContext } = responseContext;
245
+ let session = responseContext.session;
246
+ // Update our stored context if it was modified by beforeRespond hook
247
+ const storedContext = this.responsePipeline.getStoredContext();
248
+ if (storedContext !== undefined) {
249
+ try {
250
+ await this.agent.updateContext(storedContext);
251
+ }
252
+ catch (error) {
253
+ throw ResponseGenerationError.fromError(error, 'context_update_from_pipeline', params, { storedContext });
254
+ }
255
+ }
256
+ // Merge agent's collected data into session (agent data takes precedence)
257
+ const collectedData = this.agent.getCollectedData();
258
+ if (Object.keys(collectedData).length > 0) {
259
+ try {
260
+ session = (0, utils_1.mergeCollected)(session, collectedData);
261
+ utils_1.logger.debug("[ResponseModal] Merged agent collected data into session:", collectedData);
262
+ }
263
+ catch (error) {
264
+ throw ResponseGenerationError.fromError(error, 'data_merging', params, { collectedData });
265
+ }
266
+ }
267
+ // PHASE 1: PREPARE - Execute prepare function if current step has one
268
+ try {
269
+ await this.executeStepPrepare(session, effectiveContext);
270
+ }
271
+ catch (error) {
272
+ throw ResponseGenerationError.fromError(error, 'step_preparation', params, { session, effectiveContext });
273
+ }
274
+ // PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use
275
+ let routingResult;
276
+ try {
277
+ routingResult = await this.handleUnifiedRoutingAndStepSelection({
278
+ session,
279
+ history: historyEvents,
280
+ context: effectiveContext,
281
+ signal,
282
+ });
283
+ }
284
+ catch (error) {
285
+ throw ResponseGenerationError.fromError(error, 'routing_and_step_selection', params, { session, effectiveContext });
286
+ }
287
+ return {
288
+ effectiveContext,
289
+ session: routingResult.session,
290
+ history,
291
+ selectedRoute: routingResult.selectedRoute,
292
+ selectedStep: routingResult.selectedStep,
293
+ responseDirectives: routingResult.responseDirectives,
294
+ isRouteComplete: routingResult.isRouteComplete,
295
+ };
296
+ }
297
+ catch (error) {
298
+ // Re-throw ResponseGenerationError as-is, wrap others
299
+ if (ResponseGenerationError.isResponseGenerationError(error)) {
300
+ throw error;
301
+ }
302
+ throw ResponseGenerationError.fromError(error, 'preparation', params);
303
+ }
304
+ }
305
+ /**
306
+ * Unified routing and step selection logic using ResponsePipeline for optimization
307
+ * @private
308
+ */
309
+ async handleUnifiedRoutingAndStepSelection(params) {
310
+ try {
311
+ // Use the ResponsePipeline for optimized routing and step selection
312
+ // This avoids duplicate logic and leverages existing optimizations
313
+ // ResponsePipeline expects Event[] for history
314
+ const routingResult = await this.responsePipeline.handleRoutingAndStepSelection({
315
+ session: params.session,
316
+ history: params.history, // Already Event[]
317
+ context: params.context,
318
+ signal: params.signal,
319
+ });
320
+ // Determine next step using pipeline method for consistency
321
+ const stepResult = this.responsePipeline.determineNextStep({
322
+ selectedRoute: routingResult.selectedRoute,
323
+ selectedStep: routingResult.selectedStep,
324
+ session: routingResult.session,
325
+ isRouteComplete: routingResult.isRouteComplete,
326
+ });
327
+ return {
328
+ selectedRoute: routingResult.selectedRoute,
329
+ selectedStep: stepResult.nextStep, // Use the determined next step
330
+ responseDirectives: routingResult.responseDirectives,
331
+ session: stepResult.session,
332
+ isRouteComplete: routingResult.isRouteComplete,
333
+ };
334
+ }
335
+ catch (error) {
336
+ throw ResponseGenerationError.fromError(error, 'routing_optimization', params);
337
+ }
338
+ }
339
+ /**
340
+ * Unified response generation for non-streaming responses
341
+ * @private
342
+ */
343
+ async generateUnifiedResponse(responseContext) {
344
+ const { effectiveContext, session: initialSession, history, selectedRoute, selectedStep, responseDirectives, isRouteComplete } = responseContext;
345
+ let session = initialSession;
346
+ // Get last user message (needed for both route and completion handling)
347
+ // Convert HistoryItem[] to Event[] for internal processing
348
+ const historyEvents = (0, utils_1.historyToEvents)(history);
349
+ const lastMessageText = (0, utils_1.getLastMessageFromHistory)(historyEvents);
350
+ let message;
351
+ let toolCalls = undefined;
352
+ if (selectedRoute && !isRouteComplete) {
353
+ // Handle normal route processing
354
+ const result = await this.processRouteResponse({
355
+ selectedRoute,
356
+ selectedStep,
357
+ responseDirectives,
358
+ session,
359
+ history,
360
+ context: effectiveContext,
361
+ lastMessageText,
362
+ historyEvents,
363
+ signal: responseContext.history ? undefined : undefined, // TODO: Fix signal passing
364
+ });
365
+ message = result.message;
366
+ toolCalls = result.toolCalls;
367
+ session = result.session;
368
+ }
369
+ else if (isRouteComplete && selectedRoute) {
370
+ // Handle route completion
371
+ message = await this.handleRouteCompletion({
372
+ selectedRoute,
373
+ session,
374
+ context: effectiveContext,
375
+ lastMessageText,
376
+ historyEvents,
377
+ });
378
+ // Set step to END_ROUTE marker
379
+ session = (0, utils_1.enterStep)(session, constants_1.END_ROUTE_ID, "Route completed");
380
+ utils_1.logger.debug(`[ResponseModal] Route ${selectedRoute.title} completed. Entered END_ROUTE step.`);
381
+ }
382
+ else {
383
+ // Fallback: No routes defined, generate a simple response
384
+ message = await this.generateFallbackResponse({
385
+ history: historyEvents, // Use Event[] for fallback response
386
+ context: effectiveContext,
387
+ session,
388
+ });
389
+ }
390
+ return {
391
+ message,
392
+ session,
393
+ toolCalls,
394
+ isRouteComplete,
395
+ };
396
+ }
397
+ /**
398
+ * Unified streaming response generation
399
+ * @private
400
+ */
401
+ async *generateUnifiedStreamingResponse(responseContext) {
402
+ const { effectiveContext, session: initialSession, history, selectedRoute, selectedStep, responseDirectives, isRouteComplete } = responseContext;
403
+ const session = initialSession;
404
+ // Get last user message (needed for both route and completion handling)
405
+ // Convert HistoryItem[] to Event[] for internal processing
406
+ const historyEvents = (0, utils_1.historyToEvents)(history);
407
+ const lastMessageText = (0, utils_1.getLastMessageFromHistory)(historyEvents);
408
+ if (selectedRoute && !isRouteComplete) {
409
+ // Handle normal route processing with streaming
410
+ yield* this.processRouteStreamingResponse({
411
+ selectedRoute,
412
+ selectedStep,
413
+ responseDirectives,
414
+ session,
415
+ history,
416
+ context: effectiveContext,
417
+ lastMessageText,
418
+ historyEvents,
419
+ });
420
+ }
421
+ else if (isRouteComplete && selectedRoute) {
422
+ // Handle route completion streaming
423
+ yield* this.streamRouteCompletion({
424
+ selectedRoute,
425
+ session,
426
+ context: effectiveContext,
427
+ lastMessageText,
428
+ historyEvents,
429
+ });
430
+ }
431
+ else {
432
+ // Fallback: No routes defined, stream a simple response
433
+ yield* this.streamFallbackResponse({
434
+ history: historyEvents, // Use Event[] for fallback response
435
+ context: effectiveContext,
436
+ session,
437
+ });
438
+ }
439
+ }
440
+ /**
441
+ * Execute prepare function for current step if available
442
+ * @private
443
+ */
444
+ async executeStepPrepare(session, context) {
445
+ if (session.currentRoute && session.currentStep) {
446
+ const currentRoute = this.agent.getRoutes().find((r) => r.id === session.currentRoute?.id);
447
+ if (currentRoute) {
448
+ const currentStep = currentRoute.getStep(session.currentStep.id);
449
+ if (currentStep?.prepare) {
450
+ utils_1.logger.debug(`[ResponseModal] Executing prepare for step: ${currentStep.id}`);
451
+ await this.executePrepareFinalize(currentStep.prepare, context, session.data, currentRoute, currentStep);
452
+ }
453
+ }
454
+ }
455
+ }
456
+ /**
457
+ * Execute finalize function for current step if available
458
+ * @private
459
+ */
460
+ async executeStepFinalize(session, context) {
461
+ if (session.currentRoute && session.currentStep) {
462
+ const currentRoute = this.agent.getRoutes().find((r) => r.id === session.currentRoute?.id);
463
+ if (currentRoute) {
464
+ const currentStep = currentRoute.getStep(session.currentStep.id);
465
+ if (currentStep?.finalize) {
466
+ utils_1.logger.debug(`[ResponseModal] Executing finalize for step: ${currentStep.id}`);
467
+ await this.executePrepareFinalize(currentStep.finalize, context, session.data, currentRoute, currentStep);
468
+ }
469
+ }
470
+ }
471
+ }
472
+ /**
473
+ * Process route response with unified tool execution and data collection
474
+ * @private
475
+ */
476
+ async processRouteResponse(params) {
477
+ const { selectedRoute, selectedStep, responseDirectives, history, context, lastMessageText, historyEvents, signal } = params;
478
+ let session = params.session;
479
+ // Determine next step
480
+ let nextStep;
481
+ if (selectedStep) {
482
+ nextStep = selectedStep;
483
+ }
484
+ else {
485
+ // New route or no step selected - get initial step or first valid step
486
+ const routingEngine = this.agent.getRoutingEngine();
487
+ const candidates = routingEngine.getCandidateSteps(selectedRoute, undefined, session.data || {});
488
+ if (candidates.length > 0) {
489
+ nextStep = candidates[0].step;
490
+ utils_1.logger.debug(`[ResponseModal] Using first valid step: ${nextStep.id} for new route`);
491
+ }
492
+ else {
493
+ // Fallback to initial step even if it should be skipped
494
+ nextStep = selectedRoute.initialStep;
495
+ utils_1.logger.warn(`[ResponseModal] No valid steps found, using initial step: ${nextStep.id}`);
496
+ }
497
+ }
498
+ // Update session with next step
499
+ session = (0, utils_1.enterStep)(session, nextStep.id, nextStep.description);
500
+ utils_1.logger.debug(`[ResponseModal] Entered step: ${nextStep.id}`);
501
+ // Build response schema for this route (with collect fields from step)
502
+ const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, nextStep, this.agent.getSchema());
503
+ // Build response prompt
504
+ const responsePrompt = await this.responseEngine.buildResponsePrompt({
505
+ route: selectedRoute,
506
+ currentStep: nextStep,
507
+ rules: selectedRoute.getRules(),
508
+ prohibitions: selectedRoute.getProhibitions(),
509
+ directives: responseDirectives,
510
+ history: historyEvents, // Use Event[] for buildResponsePrompt
511
+ lastMessage: lastMessageText, // Use string for buildResponsePrompt
512
+ agentOptions: this.agent.getAgentOptions(),
513
+ combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
514
+ combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
515
+ context,
516
+ session,
517
+ agentSchema: this.agent.getSchema(),
518
+ });
519
+ // Collect available tools for AI
520
+ const availableTools = this.collectAvailableTools(selectedRoute, nextStep);
521
+ // Generate message using AI provider
522
+ const agentOptions = this.agent.getAgentOptions();
523
+ const result = await agentOptions.provider.generateMessage({
524
+ prompt: responsePrompt,
525
+ history: historyEvents, // Use Event[] for AI provider
526
+ context,
527
+ tools: availableTools,
528
+ signal,
529
+ parameters: responseSchema ? { jsonSchema: responseSchema, schemaName: "response_output" } : undefined,
530
+ });
531
+ let message = result.structured?.message || result.message;
532
+ let toolCalls = result.structured?.toolCalls;
533
+ // Execute tools with unified loop handling
534
+ const toolResult = await this.executeUnifiedToolLoop({
535
+ toolCalls,
536
+ context,
537
+ session,
538
+ history,
539
+ selectedRoute,
540
+ responsePrompt,
541
+ availableTools,
542
+ responseSchema,
543
+ signal,
544
+ });
545
+ session = toolResult.session;
546
+ toolCalls = toolResult.finalToolCalls;
547
+ if (toolResult.finalMessage) {
548
+ message = toolResult.finalMessage;
549
+ }
550
+ // Collect data from response
551
+ session = await this.collectDataFromResponse({ result, selectedRoute, nextStep, session });
552
+ return { message, toolCalls, session };
553
+ }
554
+ /**
555
+ * Process route streaming response with unified tool execution and data collection
556
+ * @private
557
+ */
558
+ async *processRouteStreamingResponse(params) {
559
+ const { selectedRoute, selectedStep, responseDirectives, history, context, lastMessageText, historyEvents, signal } = params;
560
+ let session = params.session;
561
+ // Determine next step (same logic as non-streaming)
562
+ let nextStep;
563
+ if (selectedStep) {
564
+ nextStep = selectedStep;
565
+ }
566
+ else {
567
+ const routingEngine = this.agent.getRoutingEngine();
568
+ const candidates = routingEngine.getCandidateSteps(selectedRoute, undefined, session.data || {});
569
+ if (candidates.length > 0) {
570
+ nextStep = candidates[0].step;
571
+ utils_1.logger.debug(`[ResponseModal] Using first valid step: ${nextStep.id} for new route`);
572
+ }
573
+ else {
574
+ nextStep = selectedRoute.initialStep;
575
+ utils_1.logger.warn(`[ResponseModal] No valid steps found, using initial step: ${nextStep.id}`);
576
+ }
577
+ }
578
+ // Update session with next step
579
+ session = (0, utils_1.enterStep)(session, nextStep.id, nextStep.description);
580
+ utils_1.logger.debug(`[ResponseModal] Entered step: ${nextStep.id}`);
581
+ // Build response schema and prompt (same as non-streaming)
582
+ const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, nextStep, this.agent.getSchema());
583
+ const responsePrompt = await this.responseEngine.buildResponsePrompt({
584
+ route: selectedRoute,
585
+ currentStep: nextStep,
586
+ rules: selectedRoute.getRules(),
587
+ prohibitions: selectedRoute.getProhibitions(),
588
+ directives: responseDirectives,
589
+ history: historyEvents, // Use Event[] for buildResponsePrompt
590
+ lastMessage: lastMessageText, // Use string for buildResponsePrompt
591
+ agentOptions: this.agent.getAgentOptions(),
592
+ combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
593
+ combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
594
+ context,
595
+ session,
596
+ agentSchema: this.agent.getSchema(),
597
+ });
598
+ // Collect available tools for AI
599
+ const availableTools = this.collectAvailableTools(selectedRoute, nextStep);
600
+ // Generate message stream using AI provider
601
+ const agentOptions = this.agent.getAgentOptions();
602
+ const stream = agentOptions.provider.generateMessageStream({
603
+ prompt: responsePrompt,
604
+ history: historyEvents, // Use Event[] for AI provider
605
+ context,
606
+ tools: availableTools,
607
+ signal,
608
+ parameters: { jsonSchema: responseSchema, schemaName: "response_stream_output" },
609
+ });
610
+ // Stream chunks with unified tool handling
611
+ for await (const chunk of stream) {
612
+ let toolCalls = undefined;
613
+ // Extract tool calls from AI response on final chunk
614
+ if (chunk.done && chunk.structured?.toolCalls) {
615
+ toolCalls = chunk.structured.toolCalls;
616
+ // Execute tools with unified loop handling
617
+ const toolResult = await this.executeUnifiedToolLoop({
618
+ toolCalls,
619
+ context,
620
+ session,
621
+ history,
622
+ selectedRoute,
623
+ responsePrompt,
624
+ availableTools,
625
+ responseSchema,
626
+ signal,
627
+ });
628
+ session = toolResult.session;
629
+ toolCalls = toolResult.finalToolCalls;
630
+ }
631
+ // Extract collected data on final chunk
632
+ if (chunk.done && chunk.structured && nextStep.collect) {
633
+ session = await this.collectDataFromResponse({
634
+ result: { structured: chunk.structured },
635
+ selectedRoute,
636
+ nextStep,
637
+ session,
638
+ });
639
+ }
640
+ // Handle session finalization on final chunk
641
+ if (chunk.done) {
642
+ await this.finalizeSession(session, context);
643
+ }
644
+ yield {
645
+ delta: chunk.delta,
646
+ accumulated: chunk.accumulated,
647
+ done: chunk.done,
648
+ session,
649
+ toolCalls,
650
+ isRouteComplete: false,
651
+ metadata: chunk.metadata,
652
+ structured: chunk.structured,
653
+ };
654
+ }
655
+ }
656
+ /**
657
+ * Unified tool execution logic with loop handling
658
+ * Consolidates the complex tool execution logic from both streaming and non-streaming responses
659
+ * @private
660
+ */
661
+ async executeUnifiedToolLoop(params) {
662
+ try {
663
+ const { context, history, selectedRoute, responsePrompt, availableTools, responseSchema, signal } = params;
664
+ let { toolCalls, session } = params;
665
+ // Convert HistoryItem[] to Event[] for internal processing
666
+ const historyEvents = (0, utils_1.historyToEvents)(history);
667
+ // Execute initial dynamic tool calls
668
+ if (toolCalls && toolCalls.length > 0) {
669
+ utils_1.logger.debug(`[ResponseModal] Executing ${toolCalls.length} dynamic tool calls`);
670
+ for (const toolCall of toolCalls) {
671
+ const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
672
+ if (!tool) {
673
+ utils_1.logger.warn(`[ResponseModal] Tool not found: ${toolCall.toolName}`);
674
+ continue;
675
+ }
676
+ try {
677
+ // Use ToolManager for unified tool execution
678
+ const toolManager = this.getToolManager();
679
+ let toolResult;
680
+ if (toolManager) {
681
+ toolResult = await toolManager.executeTool({
682
+ tool: tool,
683
+ context,
684
+ updateContext: this.agent.updateContext.bind(this.agent),
685
+ updateData: this.agent.updateCollectedData.bind(this.agent),
686
+ history: historyEvents, // Use Event[] for tool execution
687
+ data: session.data,
688
+ toolArguments: toolCall.arguments,
689
+ });
690
+ }
691
+ else {
692
+ // Fallback: execute tool directly if ToolManager not available
693
+ throw new Error(`ToolManager not available for tool execution: ${toolCall.toolName}`);
694
+ }
695
+ // Check if tool execution was successful
696
+ if (!toolResult.success) {
697
+ utils_1.logger.error(`[ResponseModal] Tool execution failed: ${toolCall.toolName} - ${toolResult.error}`);
698
+ // Continue with other tools rather than failing completely
699
+ continue;
700
+ }
701
+ // Update context with tool results
702
+ if (toolResult.contextUpdate) {
703
+ try {
704
+ await this.agent.updateContext(toolResult.contextUpdate);
705
+ }
706
+ catch (error) {
707
+ utils_1.logger.error(`[ResponseModal] Failed to update context from tool ${toolCall.toolName}:`, error);
708
+ // Continue execution but log the error
709
+ }
710
+ }
711
+ // Update collected data with tool results
712
+ if (toolResult.dataUpdate) {
713
+ try {
714
+ const updateDataMethod = this.agent.getUpdateDataMethod();
715
+ session = await updateDataMethod(session, toolResult.dataUpdate);
716
+ utils_1.logger.debug(`[ResponseModal] Tool updated collected data:`, toolResult.dataUpdate);
717
+ }
718
+ catch (error) {
719
+ utils_1.logger.error(`[ResponseModal] Failed to update data from tool ${toolCall.toolName}:`, error);
720
+ // Continue execution but log the error
721
+ }
722
+ }
723
+ utils_1.logger.debug(`[ResponseModal] Executed dynamic tool: ${toolCall.toolName} (success: ${toolResult.success})`);
724
+ }
725
+ catch (error) {
726
+ utils_1.logger.error(`[ResponseModal] Tool execution error for ${toolCall.toolName}:`, error);
727
+ // Continue with other tools rather than failing the entire response
728
+ continue;
729
+ }
730
+ }
731
+ }
732
+ // TOOL LOOP: Allow AI to make follow-up tool calls after initial tool execution
733
+ const MAX_TOOL_LOOPS = this.options?.maxToolLoops || 5;
734
+ let toolLoopCount = 0;
735
+ let hasToolCalls = toolCalls && toolCalls.length > 0;
736
+ let finalMessage;
737
+ while (hasToolCalls && toolLoopCount < MAX_TOOL_LOOPS) {
738
+ toolLoopCount++;
739
+ utils_1.logger.debug(`[ResponseModal] Starting tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS}`);
740
+ // Create tool result events with proper Event format structure
741
+ const toolResultEvents = [];
742
+ for (const toolCall of toolCalls || []) {
743
+ const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
744
+ if (tool) {
745
+ // Create proper Event format for tool results
746
+ const toolResultEvent = {
747
+ kind: types_1.EventKind.TOOL,
748
+ source: types_1.MessageRole.AGENT,
749
+ timestamp: new Date().toISOString(),
750
+ data: {
751
+ tool_calls: [
752
+ {
753
+ tool_id: toolCall.toolName,
754
+ arguments: toolCall.arguments,
755
+ result: {
756
+ data: "Tool executed successfully",
757
+ },
758
+ },
759
+ ],
760
+ },
761
+ };
762
+ toolResultEvents.push(toolResultEvent);
763
+ }
764
+ }
765
+ // Create updated history with tool results (combine Event arrays)
766
+ const updatedHistoryEvents = [...historyEvents, ...toolResultEvents];
767
+ // Make follow-up AI call to see if more tools are needed
768
+ const agentOptions = this.agent.getAgentOptions();
769
+ const followUpResult = await agentOptions.provider.generateMessage({
770
+ prompt: responsePrompt,
771
+ history: updatedHistoryEvents, // Use Event[] for AI provider
772
+ context,
773
+ tools: availableTools,
774
+ parameters: responseSchema ? {
775
+ jsonSchema: responseSchema,
776
+ schemaName: "tool_followup",
777
+ } : undefined,
778
+ signal,
779
+ });
780
+ // Check if follow-up call has more tool calls
781
+ const followUpToolCalls = followUpResult.structured?.toolCalls;
782
+ hasToolCalls = followUpToolCalls && followUpToolCalls.length > 0;
783
+ if (hasToolCalls) {
784
+ utils_1.logger.debug(`[ResponseModal] Follow-up call produced ${followUpToolCalls.length} additional tool calls`);
785
+ // Execute the follow-up tool calls
786
+ for (const toolCall of followUpToolCalls) {
787
+ const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
788
+ if (!tool) {
789
+ utils_1.logger.warn(`[ResponseModal] Tool not found in follow-up: ${toolCall.toolName}`);
790
+ continue;
791
+ }
792
+ try {
793
+ // Use ToolManager for unified tool execution
794
+ const toolManager = this.getToolManager();
795
+ let toolResult;
796
+ if (toolManager) {
797
+ toolResult = await toolManager.executeTool({
798
+ tool: tool,
799
+ context,
800
+ updateContext: this.agent.updateContext.bind(this.agent),
801
+ updateData: this.agent.updateCollectedData.bind(this.agent),
802
+ history: updatedHistoryEvents, // Use Event[] for tool execution
803
+ data: session.data,
804
+ toolArguments: toolCall.arguments,
805
+ });
806
+ }
807
+ else {
808
+ // Fallback: execute tool directly if ToolManager not available
809
+ throw new Error(`ToolManager not available for follow-up tool execution: ${toolCall.toolName}`);
810
+ }
811
+ // Check if tool execution was successful
812
+ if (!toolResult.success) {
813
+ utils_1.logger.error(`[ResponseModal] Follow-up tool execution failed: ${toolCall.toolName} - ${toolResult.error}`);
814
+ continue;
815
+ }
816
+ // Update context with follow-up tool results
817
+ if (toolResult.contextUpdate) {
818
+ try {
819
+ await this.agent.updateContext(toolResult.contextUpdate);
820
+ }
821
+ catch (error) {
822
+ utils_1.logger.error(`[ResponseModal] Failed to update context from follow-up tool ${toolCall.toolName}:`, error);
823
+ }
824
+ }
825
+ if (toolResult.dataUpdate) {
826
+ try {
827
+ const updateDataMethod = this.agent.getUpdateDataMethod();
828
+ session = await updateDataMethod(session, toolResult.dataUpdate);
829
+ utils_1.logger.debug(`[ResponseModal] Follow-up tool updated collected data:`, toolResult.dataUpdate);
830
+ }
831
+ catch (error) {
832
+ utils_1.logger.error(`[ResponseModal] Failed to update data from follow-up tool ${toolCall.toolName}:`, error);
833
+ }
834
+ }
835
+ utils_1.logger.debug(`[ResponseModal] Executed follow-up tool: ${toolCall.toolName} (success: ${toolResult.success})`);
836
+ }
837
+ catch (error) {
838
+ utils_1.logger.error(`[ResponseModal] Follow-up tool execution error for ${toolCall.toolName}:`, error);
839
+ continue;
840
+ }
841
+ }
842
+ // Update toolCalls for next iteration or final response
843
+ toolCalls = followUpToolCalls;
844
+ }
845
+ else {
846
+ utils_1.logger.debug(`[ResponseModal] Tool loop completed after ${toolLoopCount} iterations`);
847
+ // Update final message and toolCalls from follow-up result if no more tools
848
+ finalMessage = followUpResult.structured?.message || followUpResult.message;
849
+ toolCalls = followUpToolCalls || [];
850
+ break;
851
+ }
852
+ }
853
+ if (toolLoopCount >= MAX_TOOL_LOOPS) {
854
+ utils_1.logger.warn(`[ResponseModal] Tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`);
855
+ }
856
+ return {
857
+ session,
858
+ finalToolCalls: toolCalls,
859
+ finalMessage,
860
+ };
861
+ }
862
+ catch (error) {
863
+ throw ResponseGenerationError.fromError(error, 'tool_execution', params, {
864
+ toolCallsCount: params.toolCalls?.length || 0,
865
+ availableToolsCount: params.availableTools.length
866
+ });
867
+ }
868
+ } /**
869
+ * Unified data collection from AI response
870
+ * @private
871
+ */
872
+ async collectDataFromResponse(params) {
873
+ try {
874
+ const { result, selectedRoute, nextStep, session } = params;
875
+ let updatedSession = session;
876
+ // Extract collected data from final response (only for route-based interactions)
877
+ if (selectedRoute && result.structured && nextStep?.collect) {
878
+ try {
879
+ const collectedData = {};
880
+ // AgentStructuredResponse extends Record<string, unknown>, so we can safely access properties
881
+ const structuredData = result.structured;
882
+ for (const field of nextStep.collect) {
883
+ const fieldKey = String(field);
884
+ if (fieldKey in structuredData) {
885
+ collectedData[fieldKey] = structuredData[fieldKey];
886
+ }
887
+ }
888
+ // Merge collected data into session using agent-level data validation
889
+ if (Object.keys(collectedData).length > 0) {
890
+ try {
891
+ // Update agent-level collected data with validation
892
+ await this.agent.updateCollectedData(collectedData);
893
+ // Update session with validated data
894
+ const updateDataMethod = this.agent.getUpdateDataMethod();
895
+ updatedSession = await updateDataMethod(updatedSession, collectedData);
896
+ utils_1.logger.debug(`[ResponseModal] Collected data:`, collectedData);
897
+ }
898
+ catch (error) {
899
+ utils_1.logger.error(`[ResponseModal] Failed to update collected data:`, error);
900
+ // Continue without updating data rather than failing completely
901
+ }
902
+ }
903
+ }
904
+ catch (error) {
905
+ utils_1.logger.error(`[ResponseModal] Error during data collection:`, error);
906
+ // Continue without collecting data rather than failing completely
907
+ }
908
+ }
909
+ // Extract any additional data from structured response
910
+ // Since AgentStructuredResponse extends Record<string, unknown>, we can safely check for additional properties
911
+ if (result.structured && "contextUpdate" in result.structured) {
912
+ try {
913
+ const contextUpdate = result.structured.contextUpdate;
914
+ if (contextUpdate) {
915
+ await this.agent.updateContext(contextUpdate);
916
+ }
917
+ }
918
+ catch (error) {
919
+ utils_1.logger.error(`[ResponseModal] Failed to update context from structured response:`, error);
920
+ // Continue without updating context rather than failing completely
921
+ }
922
+ }
923
+ return updatedSession;
924
+ }
925
+ catch (error) {
926
+ utils_1.logger.error(`[ResponseModal] Error in collectDataFromResponse:`, error);
927
+ // Return original session if data collection fails completely
928
+ return params.session;
929
+ }
930
+ }
931
+ /**
932
+ * Handle route completion logic
933
+ * @private
934
+ */
935
+ async handleRouteCompletion(params) {
936
+ const { selectedRoute, session, context, lastMessageText, historyEvents, signal } = params;
937
+ // Get endStep spec from route
938
+ const endStepSpec = selectedRoute.endStepSpec;
939
+ // Create a temporary step for completion message generation using endStep configuration
940
+ const completionStep = new Step_1.Step(selectedRoute.id, {
941
+ description: endStepSpec.description,
942
+ id: endStepSpec.id || constants_1.END_ROUTE_ID,
943
+ collect: endStepSpec.collect,
944
+ requires: endStepSpec.requires,
945
+ prompt: endStepSpec.prompt || "Summarize what was accomplished and confirm completion based on the conversation history and collected data",
946
+ });
947
+ // Build response schema for completion
948
+ const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, completionStep, this.agent.getSchema());
949
+ const templateContext = { context, session, history: historyEvents }; // Use Event[] for template context
950
+ // Build completion response prompt
951
+ const completionPrompt = await this.responseEngine.buildResponsePrompt({
952
+ route: selectedRoute,
953
+ currentStep: completionStep,
954
+ rules: selectedRoute.getRules(),
955
+ prohibitions: selectedRoute.getProhibitions(),
956
+ directives: undefined, // No directives for completion
957
+ history: historyEvents, // Use Event[] for buildResponsePrompt
958
+ lastMessage: lastMessageText, // Use string for buildResponsePrompt
959
+ agentOptions: this.agent.getAgentOptions(),
960
+ combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
961
+ combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
962
+ context,
963
+ session,
964
+ agentSchema: this.agent.getSchema(),
965
+ });
966
+ // Generate completion message using AI provider
967
+ const agentOptions = this.agent.getAgentOptions();
968
+ const completionResult = await agentOptions.provider.generateMessage({
969
+ prompt: completionPrompt,
970
+ history: historyEvents, // Use Event[] for AI provider
971
+ context,
972
+ signal,
973
+ parameters: { jsonSchema: responseSchema, schemaName: "completion_message" },
974
+ });
975
+ const message = completionResult.structured?.message || completionResult.message;
976
+ utils_1.logger.debug(`[ResponseModal] Generated completion message for route: ${selectedRoute.title}`);
977
+ // Check for onComplete transition
978
+ const transitionConfig = await selectedRoute.evaluateOnComplete({ data: session.data }, context);
979
+ if (transitionConfig) {
980
+ // Find target route by ID or title
981
+ const targetRoute = this.agent.getRoutes().find((r) => r.id === transitionConfig.nextStep || r.title === transitionConfig.nextStep);
982
+ if (targetRoute) {
983
+ const renderedCondition = await (0, utils_1.render)(transitionConfig.condition, templateContext);
984
+ // Set pending transition in session
985
+ session.pendingTransition = {
986
+ targetRouteId: targetRoute.id,
987
+ condition: renderedCondition,
988
+ reason: "route_complete",
989
+ };
990
+ utils_1.logger.debug(`[ResponseModal] Route ${selectedRoute.title} completed with pending transition to: ${targetRoute.title}`);
991
+ }
992
+ else {
993
+ utils_1.logger.warn(`[ResponseModal] Route ${selectedRoute.title} completed but target route not found: ${transitionConfig.nextStep}`);
994
+ }
995
+ }
996
+ return message;
997
+ }
998
+ /**
999
+ * Stream route completion response
1000
+ * @private
1001
+ */
1002
+ async *streamRouteCompletion(params) {
1003
+ const { selectedRoute, context, lastMessageText, historyEvents, signal } = params;
1004
+ let session = params.session;
1005
+ // Get endStep spec from route
1006
+ const endStepSpec = selectedRoute.endStepSpec;
1007
+ // Create a temporary step for completion message generation using endStep configuration
1008
+ const completionStep = new Step_1.Step(selectedRoute.id, {
1009
+ description: endStepSpec.description,
1010
+ id: endStepSpec.id || constants_1.END_ROUTE_ID,
1011
+ collect: endStepSpec.collect,
1012
+ requires: endStepSpec.requires,
1013
+ prompt: endStepSpec.prompt || "Summarize what was accomplished and confirm completion based on the conversation history and collected data",
1014
+ });
1015
+ // Build response schema for completion
1016
+ const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, completionStep, this.agent.getSchema());
1017
+ const templateContext = { context, session, history: historyEvents }; // Use Event[] for template context
1018
+ // Build completion response prompt
1019
+ const completionPrompt = await this.responseEngine.buildResponsePrompt({
1020
+ route: selectedRoute,
1021
+ currentStep: completionStep,
1022
+ rules: selectedRoute.getRules(),
1023
+ prohibitions: selectedRoute.getProhibitions(),
1024
+ directives: undefined, // No directives for completion
1025
+ history: historyEvents, // Use Event[] for buildResponsePrompt
1026
+ lastMessage: lastMessageText, // Use string for buildResponsePrompt
1027
+ agentOptions: this.agent.getAgentOptions(),
1028
+ combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
1029
+ combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
1030
+ context,
1031
+ session,
1032
+ agentSchema: this.agent.getSchema(),
1033
+ });
1034
+ // Stream completion message using AI provider
1035
+ const agentOptions = this.agent.getAgentOptions();
1036
+ const stream = agentOptions.provider.generateMessageStream({
1037
+ prompt: completionPrompt,
1038
+ history: historyEvents, // Use Event[] for AI provider
1039
+ context,
1040
+ signal,
1041
+ parameters: { jsonSchema: responseSchema, schemaName: "completion_message_stream" },
1042
+ });
1043
+ utils_1.logger.debug(`[ResponseModal] Streaming completion message for route: ${selectedRoute.title}`);
1044
+ // Check for onComplete transition
1045
+ const transitionConfig = await selectedRoute.evaluateOnComplete({ data: session.data }, context);
1046
+ if (transitionConfig) {
1047
+ // Find target route by ID or title
1048
+ const targetRoute = this.agent.getRoutes().find((r) => r.id === transitionConfig.nextStep || r.title === transitionConfig.nextStep);
1049
+ if (targetRoute) {
1050
+ const renderedCondition = await (0, utils_1.render)(transitionConfig.condition, templateContext);
1051
+ // Set pending transition in session
1052
+ session = {
1053
+ ...session,
1054
+ pendingTransition: {
1055
+ targetRouteId: targetRoute.id,
1056
+ condition: renderedCondition,
1057
+ reason: "route_complete",
1058
+ },
1059
+ };
1060
+ utils_1.logger.debug(`[ResponseModal] Route ${selectedRoute.title} completed with pending transition to: ${targetRoute.title}`);
1061
+ }
1062
+ else {
1063
+ utils_1.logger.warn(`[ResponseModal] Route ${selectedRoute.title} completed but target route not found: ${transitionConfig.nextStep}`);
1064
+ }
1065
+ }
1066
+ // Set step to END_ROUTE marker
1067
+ session = (0, utils_1.enterStep)(session, constants_1.END_ROUTE_ID, "Route completed");
1068
+ utils_1.logger.debug(`[ResponseModal] Route ${selectedRoute.title} completed. Entered END_ROUTE step.`);
1069
+ // Stream completion chunks
1070
+ for await (const chunk of stream) {
1071
+ // Update current session if we have one
1072
+ if (chunk.done) {
1073
+ await this.finalizeSession(session, context);
1074
+ }
1075
+ yield {
1076
+ delta: chunk.delta,
1077
+ accumulated: chunk.accumulated,
1078
+ done: chunk.done,
1079
+ session,
1080
+ toolCalls: undefined,
1081
+ isRouteComplete: true,
1082
+ metadata: chunk.metadata,
1083
+ structured: chunk.structured,
1084
+ };
1085
+ }
1086
+ }
1087
+ /**
1088
+ * Generate fallback response when no routes are available
1089
+ * @private
1090
+ */
1091
+ async generateFallbackResponse(params) {
1092
+ const { history, context, session, signal } = params;
1093
+ utils_1.logger.debug(`[ResponseModal] No route selected, generating basic response`);
1094
+ // Build basic response prompt without route context
1095
+ const fallbackPrompt = await this.responseEngine.buildFallbackPrompt({
1096
+ history,
1097
+ agentOptions: this.agent.getAgentOptions(),
1098
+ terms: this.agent.getTerms(),
1099
+ guidelines: this.agent.getGuidelines(),
1100
+ context,
1101
+ session,
1102
+ });
1103
+ const agentOptions = this.agent.getAgentOptions();
1104
+ const result = await agentOptions.provider.generateMessage({
1105
+ prompt: fallbackPrompt,
1106
+ history,
1107
+ context,
1108
+ signal,
1109
+ parameters: {
1110
+ jsonSchema: {
1111
+ type: "object",
1112
+ properties: { message: { type: "string" } },
1113
+ required: ["message"],
1114
+ additionalProperties: false,
1115
+ },
1116
+ schemaName: "fallback_response",
1117
+ },
1118
+ });
1119
+ return result.structured?.message || result.message;
1120
+ }
1121
+ /**
1122
+ * Stream fallback response when no routes are available
1123
+ * @private
1124
+ */
1125
+ async *streamFallbackResponse(params) {
1126
+ const { history, context, session, signal } = params;
1127
+ const fallbackPrompt = await this.responseEngine.buildFallbackPrompt({
1128
+ history,
1129
+ agentOptions: this.agent.getAgentOptions(),
1130
+ terms: this.agent.getTerms(),
1131
+ guidelines: this.agent.getGuidelines(),
1132
+ context,
1133
+ session,
1134
+ });
1135
+ const agentOptions = this.agent.getAgentOptions();
1136
+ const stream = agentOptions.provider.generateMessageStream({
1137
+ prompt: fallbackPrompt,
1138
+ history,
1139
+ context,
1140
+ signal,
1141
+ parameters: {
1142
+ jsonSchema: {
1143
+ type: "object",
1144
+ properties: { message: { type: "string" } },
1145
+ required: ["message"],
1146
+ additionalProperties: false,
1147
+ },
1148
+ schemaName: "fallback_stream_response",
1149
+ },
1150
+ });
1151
+ for await (const chunk of stream) {
1152
+ // Update current session if we have one
1153
+ if (chunk.done) {
1154
+ await this.finalizeSession(session, context);
1155
+ }
1156
+ yield {
1157
+ delta: chunk.delta,
1158
+ accumulated: chunk.accumulated,
1159
+ done: chunk.done,
1160
+ session,
1161
+ toolCalls: undefined,
1162
+ isRouteComplete: false,
1163
+ metadata: chunk.metadata,
1164
+ structured: chunk.structured,
1165
+ };
1166
+ }
1167
+ }
1168
+ /**
1169
+ * Handle session persistence and finalization
1170
+ * @private
1171
+ */
1172
+ async finalizeSession(session, context) {
1173
+ // Auto-save session step to persistence if configured
1174
+ const persistenceManager = this.agent.getPersistenceManager();
1175
+ const agentOptions = this.agent.getAgentOptions();
1176
+ if (persistenceManager &&
1177
+ session.id &&
1178
+ (this.options?.enableAutoSave !== false && agentOptions.persistence?.autoSave !== false)) {
1179
+ await persistenceManager.saveSessionState(session.id, session);
1180
+ utils_1.logger.debug(`[ResponseModal] Auto-saved session step to persistence: ${session.id}`);
1181
+ }
1182
+ // Execute finalize function
1183
+ await this.executeStepFinalize(session, context);
1184
+ // Update current session if we have one
1185
+ const currentSession = this.agent.getCurrentSession();
1186
+ if (currentSession) {
1187
+ this.agent.setCurrentSession(session);
1188
+ }
1189
+ }
1190
+ // ============================================================================
1191
+ // UTILITY METHODS - Helper methods for tool management and other utilities
1192
+ // ============================================================================
1193
+ /**
1194
+ * Find an available tool by name for the given route using ToolManager
1195
+ * Delegates to ToolManager for unified tool resolution
1196
+ * @private
1197
+ */
1198
+ findAvailableTool(toolName, route) {
1199
+ // Use ToolManager for unified tool resolution
1200
+ const toolManager = this.getToolManager();
1201
+ if (toolManager) {
1202
+ return toolManager.find(toolName, undefined, undefined, route);
1203
+ }
1204
+ // Fallback to legacy resolution if ToolManager not available
1205
+ utils_1.logger.warn(`[ResponseModal] ToolManager not available, using legacy tool resolution for: ${toolName}`);
1206
+ // Check route-level tools first (if route provided)
1207
+ if (route) {
1208
+ const routeTool = route
1209
+ .getTools()
1210
+ .find((tool) => tool.id === toolName || tool.name === toolName);
1211
+ if (routeTool)
1212
+ return routeTool;
1213
+ }
1214
+ // Fall back to agent-level tools
1215
+ const agentTools = this.agent.getTools();
1216
+ return agentTools.find((tool) => tool.id === toolName || tool.name === toolName);
1217
+ }
1218
+ /**
1219
+ * Collect all available tools for the given route and step context using ToolManager
1220
+ * Delegates to ToolManager for unified tool resolution and deduplication
1221
+ * @private
1222
+ */
1223
+ collectAvailableTools(route, step) {
1224
+ // Use ToolManager for unified tool collection if available
1225
+ const toolManager = this.getToolManager();
1226
+ if (toolManager) {
1227
+ const availableTools = toolManager.getAvailable(undefined, step, route);
1228
+ return availableTools.map((tool) => ({
1229
+ id: tool.id,
1230
+ name: tool.name || tool.id,
1231
+ description: tool.description,
1232
+ parameters: tool.parameters,
1233
+ }));
1234
+ }
1235
+ // Fallback to legacy collection logic if ToolManager not available
1236
+ utils_1.logger.warn(`[ResponseModal] ToolManager not available, using legacy tool collection`);
1237
+ const availableTools = new Map();
1238
+ // Add agent-level tools
1239
+ this.agent.getTools().forEach((tool) => {
1240
+ availableTools.set(tool.id, tool);
1241
+ });
1242
+ // Add route-level tools (these take precedence)
1243
+ if (route) {
1244
+ route.getTools().forEach((tool) => {
1245
+ availableTools.set(tool.id, tool);
1246
+ });
1247
+ }
1248
+ // Filter by step-level allowed tools if specified
1249
+ if (step?.tools) {
1250
+ const allowedToolIds = new Set();
1251
+ const stepTools = [];
1252
+ for (const toolRef of step.tools) {
1253
+ if (typeof toolRef === "string") {
1254
+ // Reference to registered tool
1255
+ allowedToolIds.add(toolRef);
1256
+ }
1257
+ else {
1258
+ // Inline tool definition
1259
+ if (toolRef.id) {
1260
+ allowedToolIds.add(toolRef.id);
1261
+ stepTools.push(toolRef);
1262
+ }
1263
+ }
1264
+ }
1265
+ // If step specifies tools, only include those
1266
+ if (allowedToolIds.size > 0) {
1267
+ const filteredTools = new Map();
1268
+ for (const toolId of Array.from(allowedToolIds)) {
1269
+ const tool = availableTools.get(toolId);
1270
+ if (tool) {
1271
+ filteredTools.set(toolId, tool);
1272
+ }
1273
+ }
1274
+ // Add inline tools
1275
+ stepTools.forEach((tool) => {
1276
+ if (tool.id) {
1277
+ filteredTools.set(tool.id, tool);
1278
+ }
1279
+ });
1280
+ availableTools.clear();
1281
+ filteredTools.forEach((tool, id) => availableTools.set(id, tool));
1282
+ }
1283
+ }
1284
+ // Convert to the format expected by AI providers
1285
+ return Array.from(availableTools.values()).map((tool) => ({
1286
+ id: tool.id,
1287
+ name: tool.name || tool.id,
1288
+ description: tool.description,
1289
+ parameters: tool.parameters,
1290
+ }));
1291
+ }
1292
+ /**
1293
+ * Execute a prepare or finalize function/tool
1294
+ * @private
1295
+ */
1296
+ async executePrepareFinalize(prepareOrFinalize, context, data, route, step) {
1297
+ if (!prepareOrFinalize)
1298
+ return;
1299
+ if (typeof prepareOrFinalize === "function") {
1300
+ // It's a function - call it directly
1301
+ await prepareOrFinalize(context, data);
1302
+ }
1303
+ else {
1304
+ // It's a tool reference - find and execute the tool
1305
+ let tool;
1306
+ if (typeof prepareOrFinalize === "string") {
1307
+ // Tool ID - use ToolManager for unified resolution
1308
+ const toolManager = this.getToolManager();
1309
+ if (toolManager) {
1310
+ tool = toolManager.find(prepareOrFinalize, undefined, step, route);
1311
+ }
1312
+ else {
1313
+ // Fallback to legacy resolution if ToolManager not available
1314
+ utils_1.logger.warn(`[ResponseModal] ToolManager not available, using legacy tool resolution for prepare/finalize: ${prepareOrFinalize}`);
1315
+ const availableTools = new Map();
1316
+ // Add agent-level tools
1317
+ this.agent.getTools().forEach((t) => {
1318
+ availableTools.set(t.id, t);
1319
+ });
1320
+ // Add route-level tools
1321
+ if (route) {
1322
+ route.getTools().forEach((t) => {
1323
+ availableTools.set(t.id, t);
1324
+ });
1325
+ }
1326
+ // Add step-level tools
1327
+ if (step?.tools) {
1328
+ for (const toolRef of step.tools) {
1329
+ if (typeof toolRef === "string") {
1330
+ // Keep as is
1331
+ }
1332
+ else if (typeof toolRef === 'object' && 'id' in toolRef && toolRef.id) {
1333
+ availableTools.set(toolRef.id, toolRef);
1334
+ }
1335
+ }
1336
+ }
1337
+ tool = availableTools.get(prepareOrFinalize);
1338
+ }
1339
+ }
1340
+ else {
1341
+ // Tool object - use directly
1342
+ tool = prepareOrFinalize;
1343
+ }
1344
+ if (tool) {
1345
+ // Use ToolManager for unified tool execution
1346
+ const toolManager = this.getToolManager();
1347
+ let result;
1348
+ if (toolManager) {
1349
+ result = await toolManager.executeTool({
1350
+ tool,
1351
+ context,
1352
+ updateContext: this.agent.updateContext.bind(this.agent),
1353
+ updateData: this.agent.updateCollectedData.bind(this.agent),
1354
+ history: [], // Empty history for prepare/finalize
1355
+ data,
1356
+ });
1357
+ }
1358
+ else {
1359
+ // Fallback: execute tool directly if ToolManager not available
1360
+ throw new Error(`ToolManager not available for prepare/finalize tool execution: ${typeof prepareOrFinalize === "string" ? prepareOrFinalize : "inline tool"}`);
1361
+ }
1362
+ if (!result.success) {
1363
+ utils_1.logger.error(`[ResponseModal] Tool execution failed in prepare/finalize: ${result.error}`);
1364
+ throw new Error(`Tool execution failed: ${result.error}`);
1365
+ }
1366
+ }
1367
+ else {
1368
+ utils_1.logger.warn(`[ResponseModal] Tool not found for prepare/finalize: ${typeof prepareOrFinalize === "string"
1369
+ ? prepareOrFinalize
1370
+ : "inline tool"}`);
1371
+ }
1372
+ }
1373
+ }
1374
+ /**
1375
+ * Merge terms with route-specific taking precedence on conflicts
1376
+ * @private
1377
+ */
1378
+ mergeTerms(agentTerms, routeTerms) {
1379
+ const merged = new Map();
1380
+ // Add agent terms first
1381
+ agentTerms.forEach((term) => {
1382
+ const name = typeof term.name === "string" ? term.name : term.name.toString();
1383
+ merged.set(name, term);
1384
+ });
1385
+ // Add route terms (these take precedence)
1386
+ routeTerms.forEach((term) => {
1387
+ const name = typeof term.name === "string" ? term.name : term.name.toString();
1388
+ merged.set(name, term);
1389
+ });
1390
+ return Array.from(merged.values());
1391
+ }
1392
+ }
1393
+ exports.ResponseModal = ResponseModal;
1394
+ //# sourceMappingURL=ResponseModal.js.map