@falai/agent 0.9.0 → 1.0.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 (654) hide show
  1. package/README.md +304 -72
  2. package/dist/adapters/MemoryAdapter.d.ts.map +1 -0
  3. package/dist/adapters/MemoryAdapter.js.map +1 -0
  4. package/dist/adapters/MongoAdapter.d.ts.map +1 -0
  5. package/dist/adapters/MongoAdapter.js.map +1 -0
  6. package/dist/adapters/OpenSearchAdapter.d.ts.map +1 -0
  7. package/dist/adapters/OpenSearchAdapter.js.map +1 -0
  8. package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -0
  9. package/dist/adapters/PostgreSQLAdapter.js.map +1 -0
  10. package/dist/adapters/PrismaAdapter.d.ts.map +1 -0
  11. package/dist/{src/adapters → adapters}/PrismaAdapter.js +3 -2
  12. package/dist/adapters/PrismaAdapter.js.map +1 -0
  13. package/dist/adapters/RedisAdapter.d.ts.map +1 -0
  14. package/dist/{src/adapters → adapters}/RedisAdapter.js +3 -3
  15. package/dist/adapters/RedisAdapter.js.map +1 -0
  16. package/dist/adapters/SQLiteAdapter.d.ts.map +1 -0
  17. package/dist/adapters/SQLiteAdapter.js.map +1 -0
  18. package/dist/adapters/index.d.ts.map +1 -0
  19. package/dist/adapters/index.js.map +1 -0
  20. package/dist/cjs/adapters/MemoryAdapter.js.map +1 -0
  21. package/dist/cjs/adapters/MongoAdapter.js.map +1 -0
  22. package/dist/cjs/adapters/OpenSearchAdapter.js.map +1 -0
  23. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -0
  24. package/dist/{src → cjs}/adapters/PrismaAdapter.d.ts.map +1 -1
  25. package/dist/cjs/{src/adapters → adapters}/PrismaAdapter.js +3 -2
  26. package/dist/cjs/adapters/PrismaAdapter.js.map +1 -0
  27. package/dist/cjs/{src/adapters → adapters}/RedisAdapter.js +2 -2
  28. package/dist/cjs/adapters/RedisAdapter.js.map +1 -0
  29. package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -0
  30. package/dist/cjs/adapters/index.js.map +1 -0
  31. package/dist/cjs/constants/index.js.map +1 -0
  32. package/dist/{src → cjs}/core/Agent.d.ts +25 -6
  33. package/dist/cjs/core/Agent.d.ts.map +1 -0
  34. package/dist/cjs/{src/core → core}/Agent.js +121 -37
  35. package/dist/cjs/core/Agent.js.map +1 -0
  36. package/dist/cjs/core/BatchExecutor.d.ts +353 -0
  37. package/dist/cjs/core/BatchExecutor.d.ts.map +1 -0
  38. package/dist/cjs/core/BatchExecutor.js +842 -0
  39. package/dist/cjs/core/BatchExecutor.js.map +1 -0
  40. package/dist/cjs/core/BatchPromptBuilder.d.ts +86 -0
  41. package/dist/cjs/core/BatchPromptBuilder.d.ts.map +1 -0
  42. package/dist/cjs/core/BatchPromptBuilder.js +201 -0
  43. package/dist/cjs/core/BatchPromptBuilder.js.map +1 -0
  44. package/dist/cjs/core/Events.js.map +1 -0
  45. package/dist/cjs/core/PersistenceManager.js.map +1 -0
  46. package/dist/{src → cjs}/core/PromptComposer.d.ts +1 -1
  47. package/dist/cjs/core/PromptComposer.d.ts.map +1 -0
  48. package/dist/cjs/{src/core → core}/PromptComposer.js +44 -7
  49. package/dist/cjs/core/PromptComposer.js.map +1 -0
  50. package/dist/{src → cjs}/core/ResponseEngine.d.ts.map +1 -1
  51. package/dist/cjs/core/ResponseEngine.js +202 -0
  52. package/dist/cjs/core/ResponseEngine.js.map +1 -0
  53. package/dist/{src → cjs}/core/ResponseModal.d.ts +54 -3
  54. package/dist/cjs/core/ResponseModal.d.ts.map +1 -0
  55. package/dist/cjs/{src/core → core}/ResponseModal.js +807 -121
  56. package/dist/cjs/core/ResponseModal.js.map +1 -0
  57. package/dist/{src → cjs}/core/ResponsePipeline.d.ts +10 -6
  58. package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -0
  59. package/dist/cjs/{src/core → core}/ResponsePipeline.js +60 -25
  60. package/dist/cjs/core/ResponsePipeline.js.map +1 -0
  61. package/dist/{src → cjs}/core/Route.d.ts +46 -10
  62. package/dist/cjs/core/Route.d.ts.map +1 -0
  63. package/dist/cjs/core/Route.js +541 -0
  64. package/dist/cjs/core/Route.js.map +1 -0
  65. package/dist/cjs/{src/core → core}/RoutingEngine.d.ts +35 -5
  66. package/dist/cjs/core/RoutingEngine.d.ts.map +1 -0
  67. package/dist/cjs/{src/core → core}/RoutingEngine.js +360 -98
  68. package/dist/cjs/core/RoutingEngine.js.map +1 -0
  69. package/dist/{src → cjs}/core/SessionManager.d.ts +9 -1
  70. package/dist/cjs/core/SessionManager.d.ts.map +1 -0
  71. package/dist/cjs/{src/core → core}/SessionManager.js +27 -5
  72. package/dist/cjs/core/SessionManager.js.map +1 -0
  73. package/dist/cjs/core/Step.d.ts +170 -0
  74. package/dist/cjs/core/Step.d.ts.map +1 -0
  75. package/dist/cjs/core/Step.js +448 -0
  76. package/dist/cjs/core/Step.js.map +1 -0
  77. package/dist/cjs/core/ToolManager.d.ts +234 -0
  78. package/dist/cjs/core/ToolManager.d.ts.map +1 -0
  79. package/dist/cjs/core/ToolManager.js +1117 -0
  80. package/dist/cjs/core/ToolManager.js.map +1 -0
  81. package/dist/{src → cjs}/index.d.ts +5 -3
  82. package/dist/cjs/index.d.ts.map +1 -0
  83. package/dist/cjs/{src/index.js → index.js} +16 -3
  84. package/dist/cjs/index.js.map +1 -0
  85. package/dist/cjs/{src/providers → providers}/AnthropicProvider.js +18 -18
  86. package/dist/cjs/providers/AnthropicProvider.js.map +1 -0
  87. package/dist/{src → cjs}/providers/GeminiProvider.d.ts.map +1 -1
  88. package/dist/cjs/{src/providers → providers}/GeminiProvider.js +123 -51
  89. package/dist/cjs/providers/GeminiProvider.js.map +1 -0
  90. package/dist/cjs/{src/providers → providers}/OpenAIProvider.js +19 -19
  91. package/dist/cjs/providers/OpenAIProvider.js.map +1 -0
  92. package/dist/cjs/{src/providers → providers}/OpenRouterProvider.js +19 -19
  93. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -0
  94. package/dist/cjs/providers/index.js.map +1 -0
  95. package/dist/cjs/{src/types → types}/agent.d.ts +12 -4
  96. package/dist/cjs/types/agent.d.ts.map +1 -0
  97. package/dist/cjs/types/agent.js.map +1 -0
  98. package/dist/{src → cjs}/types/ai.js.map +1 -1
  99. package/dist/cjs/types/history.js.map +1 -0
  100. package/dist/cjs/{src/types → types}/index.d.ts +5 -3
  101. package/dist/{src → cjs}/types/index.d.ts.map +1 -1
  102. package/dist/cjs/{src/types → types}/index.js +8 -1
  103. package/dist/cjs/types/index.js.map +1 -0
  104. package/dist/cjs/types/persistence.js.map +1 -0
  105. package/dist/cjs/{src/types → types}/route.d.ts +116 -15
  106. package/dist/cjs/types/route.d.ts.map +1 -0
  107. package/dist/cjs/{src/types → types}/route.js.map +1 -1
  108. package/dist/cjs/types/session.js.map +1 -0
  109. package/dist/cjs/types/template.d.ts +88 -0
  110. package/dist/cjs/types/template.d.ts.map +1 -0
  111. package/dist/cjs/types/tool.d.ts +130 -0
  112. package/dist/cjs/types/tool.d.ts.map +1 -0
  113. package/dist/cjs/types/tool.js +19 -0
  114. package/dist/cjs/types/tool.js.map +1 -0
  115. package/dist/cjs/utils/clone.js.map +1 -0
  116. package/dist/cjs/utils/condition.d.ts +38 -0
  117. package/dist/cjs/utils/condition.d.ts.map +1 -0
  118. package/dist/cjs/utils/condition.js +168 -0
  119. package/dist/cjs/utils/condition.js.map +1 -0
  120. package/dist/cjs/utils/event.js.map +1 -0
  121. package/dist/cjs/utils/history.js.map +1 -0
  122. package/dist/cjs/utils/id.js.map +1 -0
  123. package/dist/cjs/{src/utils → utils}/index.d.ts +3 -1
  124. package/dist/cjs/utils/index.d.ts.map +1 -0
  125. package/dist/cjs/{src/utils → utils}/index.js +12 -1
  126. package/dist/cjs/utils/index.js.map +1 -0
  127. package/dist/cjs/utils/json.d.ts +16 -0
  128. package/dist/cjs/utils/json.d.ts.map +1 -0
  129. package/dist/cjs/utils/json.js +47 -0
  130. package/dist/cjs/utils/json.js.map +1 -0
  131. package/dist/cjs/utils/logger.js.map +1 -0
  132. package/dist/{src → cjs}/utils/retry.d.ts +0 -3
  133. package/dist/cjs/utils/retry.d.ts.map +1 -0
  134. package/dist/cjs/{src/utils → utils}/retry.js +8 -7
  135. package/dist/cjs/utils/retry.js.map +1 -0
  136. package/dist/cjs/utils/session.js.map +1 -0
  137. package/dist/{src → cjs}/utils/template.d.ts +48 -0
  138. package/dist/cjs/utils/template.d.ts.map +1 -0
  139. package/dist/cjs/{src/utils → utils}/template.js +100 -0
  140. package/dist/cjs/utils/template.js.map +1 -0
  141. package/dist/constants/index.d.ts.map +1 -0
  142. package/dist/constants/index.js.map +1 -0
  143. package/dist/{cjs/src/core → core}/Agent.d.ts +25 -6
  144. package/dist/core/Agent.d.ts.map +1 -0
  145. package/dist/{src/core → core}/Agent.js +122 -38
  146. package/dist/core/Agent.js.map +1 -0
  147. package/dist/core/BatchExecutor.d.ts +353 -0
  148. package/dist/core/BatchExecutor.d.ts.map +1 -0
  149. package/dist/core/BatchExecutor.js +837 -0
  150. package/dist/core/BatchExecutor.js.map +1 -0
  151. package/dist/core/BatchPromptBuilder.d.ts +86 -0
  152. package/dist/core/BatchPromptBuilder.d.ts.map +1 -0
  153. package/dist/core/BatchPromptBuilder.js +197 -0
  154. package/dist/core/BatchPromptBuilder.js.map +1 -0
  155. package/dist/core/Events.d.ts.map +1 -0
  156. package/dist/core/Events.js.map +1 -0
  157. package/dist/core/PersistenceManager.d.ts.map +1 -0
  158. package/dist/core/PersistenceManager.js.map +1 -0
  159. package/dist/{cjs/src/core → core}/PromptComposer.d.ts +1 -1
  160. package/dist/core/PromptComposer.d.ts.map +1 -0
  161. package/dist/{src/core → core}/PromptComposer.js +45 -8
  162. package/dist/core/PromptComposer.js.map +1 -0
  163. package/dist/core/ResponseEngine.d.ts.map +1 -0
  164. package/dist/core/ResponseEngine.js +198 -0
  165. package/dist/core/ResponseEngine.js.map +1 -0
  166. package/dist/{cjs/src/core → core}/ResponseModal.d.ts +54 -3
  167. package/dist/core/ResponseModal.d.ts.map +1 -0
  168. package/dist/{src/core → core}/ResponseModal.js +807 -121
  169. package/dist/core/ResponseModal.js.map +1 -0
  170. package/dist/{cjs/src/core → core}/ResponsePipeline.d.ts +10 -6
  171. package/dist/core/ResponsePipeline.d.ts.map +1 -0
  172. package/dist/{src/core → core}/ResponsePipeline.js +60 -25
  173. package/dist/core/ResponsePipeline.js.map +1 -0
  174. package/dist/{cjs/src/core → core}/Route.d.ts +46 -10
  175. package/dist/core/Route.d.ts.map +1 -0
  176. package/dist/core/Route.js +537 -0
  177. package/dist/core/Route.js.map +1 -0
  178. package/dist/{src/core → core}/RoutingEngine.d.ts +35 -5
  179. package/dist/core/RoutingEngine.d.ts.map +1 -0
  180. package/dist/{src/core → core}/RoutingEngine.js +343 -81
  181. package/dist/core/RoutingEngine.js.map +1 -0
  182. package/dist/{cjs/src/core → core}/SessionManager.d.ts +9 -1
  183. package/dist/core/SessionManager.d.ts.map +1 -0
  184. package/dist/{src/core → core}/SessionManager.js +27 -5
  185. package/dist/core/SessionManager.js.map +1 -0
  186. package/dist/core/Step.d.ts +170 -0
  187. package/dist/core/Step.d.ts.map +1 -0
  188. package/dist/core/Step.js +444 -0
  189. package/dist/core/Step.js.map +1 -0
  190. package/dist/core/ToolManager.d.ts +234 -0
  191. package/dist/core/ToolManager.d.ts.map +1 -0
  192. package/dist/core/ToolManager.js +1111 -0
  193. package/dist/core/ToolManager.js.map +1 -0
  194. package/dist/{cjs/src/index.d.ts → index.d.ts} +5 -3
  195. package/dist/index.d.ts.map +1 -0
  196. package/dist/{src/index.js → index.js} +4 -1
  197. package/dist/index.js.map +1 -0
  198. package/dist/providers/AnthropicProvider.d.ts.map +1 -0
  199. package/dist/{src/providers → providers}/AnthropicProvider.js +17 -17
  200. package/dist/providers/AnthropicProvider.js.map +1 -0
  201. package/dist/providers/GeminiProvider.d.ts.map +1 -0
  202. package/dist/{src/providers → providers}/GeminiProvider.js +123 -51
  203. package/dist/providers/GeminiProvider.js.map +1 -0
  204. package/dist/providers/OpenAIProvider.d.ts.map +1 -0
  205. package/dist/{src/providers → providers}/OpenAIProvider.js +18 -18
  206. package/dist/providers/OpenAIProvider.js.map +1 -0
  207. package/dist/providers/OpenRouterProvider.d.ts.map +1 -0
  208. package/dist/{src/providers → providers}/OpenRouterProvider.js +18 -18
  209. package/dist/providers/OpenRouterProvider.js.map +1 -0
  210. package/dist/providers/index.d.ts.map +1 -0
  211. package/dist/providers/index.js.map +1 -0
  212. package/dist/{src/types → types}/agent.d.ts +12 -4
  213. package/dist/types/agent.d.ts.map +1 -0
  214. package/dist/types/agent.js.map +1 -0
  215. package/dist/types/ai.d.ts.map +1 -0
  216. package/dist/types/ai.js.map +1 -0
  217. package/dist/types/history.d.ts.map +1 -0
  218. package/dist/types/history.js.map +1 -0
  219. package/dist/{src/types → types}/index.d.ts +5 -3
  220. package/dist/types/index.d.ts.map +1 -0
  221. package/dist/{src/types → types}/index.js +2 -0
  222. package/dist/types/index.js.map +1 -0
  223. package/dist/types/persistence.d.ts.map +1 -0
  224. package/dist/types/persistence.js.map +1 -0
  225. package/dist/{src/types → types}/route.d.ts +116 -15
  226. package/dist/types/route.d.ts.map +1 -0
  227. package/dist/{src/types → types}/route.js.map +1 -1
  228. package/dist/types/routing.d.ts.map +1 -0
  229. package/dist/{cjs/src/types → types}/routing.js.map +1 -1
  230. package/dist/types/schema.d.ts.map +1 -0
  231. package/dist/{cjs/src/types → types}/schema.js.map +1 -1
  232. package/dist/types/session.d.ts.map +1 -0
  233. package/dist/{src/types → types}/session.js.map +1 -1
  234. package/dist/types/template.d.ts +88 -0
  235. package/dist/types/template.d.ts.map +1 -0
  236. package/dist/{cjs/src/types → types}/template.js.map +1 -1
  237. package/dist/types/tool.d.ts +130 -0
  238. package/dist/types/tool.d.ts.map +1 -0
  239. package/dist/types/tool.js +16 -0
  240. package/dist/types/tool.js.map +1 -0
  241. package/dist/utils/clone.d.ts.map +1 -0
  242. package/dist/utils/clone.js.map +1 -0
  243. package/dist/utils/condition.d.ts +38 -0
  244. package/dist/utils/condition.d.ts.map +1 -0
  245. package/dist/utils/condition.js +161 -0
  246. package/dist/utils/condition.js.map +1 -0
  247. package/dist/utils/event.d.ts.map +1 -0
  248. package/dist/utils/event.js.map +1 -0
  249. package/dist/utils/history.d.ts.map +1 -0
  250. package/dist/utils/history.js.map +1 -0
  251. package/dist/utils/id.d.ts.map +1 -0
  252. package/dist/utils/id.js.map +1 -0
  253. package/dist/{src/utils → utils}/index.d.ts +3 -1
  254. package/dist/utils/index.d.ts.map +1 -0
  255. package/dist/{src/utils → utils}/index.js +5 -1
  256. package/dist/utils/index.js.map +1 -0
  257. package/dist/utils/json.d.ts +16 -0
  258. package/dist/utils/json.d.ts.map +1 -0
  259. package/dist/utils/json.js +43 -0
  260. package/dist/utils/json.js.map +1 -0
  261. package/dist/utils/logger.d.ts.map +1 -0
  262. package/dist/utils/logger.js.map +1 -0
  263. package/dist/{cjs/src/utils → utils}/retry.d.ts +0 -3
  264. package/dist/utils/retry.d.ts.map +1 -0
  265. package/dist/{src/utils → utils}/retry.js +5 -4
  266. package/dist/utils/retry.js.map +1 -0
  267. package/dist/utils/session.d.ts.map +1 -0
  268. package/dist/utils/session.js.map +1 -0
  269. package/dist/{cjs/src/utils → utils}/template.d.ts +48 -0
  270. package/dist/utils/template.d.ts.map +1 -0
  271. package/dist/{src/utils → utils}/template.js +98 -0
  272. package/dist/utils/template.js.map +1 -0
  273. package/docs/CONTRIBUTING.md +40 -0
  274. package/docs/README.md +12 -5
  275. package/docs/api/README.md +295 -56
  276. package/docs/api/overview.md +272 -31
  277. package/docs/architecture/data-extraction-flow.md +363 -0
  278. package/docs/architecture/multi-step-execution.md +243 -0
  279. package/docs/core/agent/README.md +120 -5
  280. package/docs/core/agent/session-management.md +153 -6
  281. package/docs/core/ai-integration/prompt-composition.md +135 -0
  282. package/docs/core/ai-integration/response-processing.md +261 -4
  283. package/docs/core/conversation-flows/data-collection.md +143 -0
  284. package/docs/core/conversation-flows/routes.md +132 -2
  285. package/docs/core/conversation-flows/step-transitions.md +132 -0
  286. package/docs/core/conversation-flows/steps.md +112 -0
  287. package/docs/core/error-handling.md +831 -0
  288. package/docs/core/routing/intelligent-routing.md +118 -0
  289. package/docs/core/tools/tool-definition.md +684 -60
  290. package/docs/core/tools/tool-scoping.md +244 -53
  291. package/docs/guides/error-handling-patterns.md +578 -0
  292. package/docs/guides/getting-started/README.md +423 -31
  293. package/docs/guides/migration/README.md +23 -0
  294. package/docs/guides/migration/flexible-routing-conditions.md +375 -0
  295. package/docs/guides/migration/multi-step-execution.md +303 -0
  296. package/examples/advanced-patterns/knowledge-based-agent.ts +107 -30
  297. package/examples/advanced-patterns/persistent-onboarding.ts +70 -48
  298. package/examples/advanced-patterns/route-lifecycle-hooks.ts +82 -12
  299. package/examples/advanced-patterns/streaming-responses.ts +2 -2
  300. package/examples/ai-providers/anthropic-integration.ts +13 -9
  301. package/examples/ai-providers/openai-integration.ts +12 -8
  302. package/examples/condition-patterns/function-only-conditions.ts +365 -0
  303. package/examples/condition-patterns/mixed-array-conditions.ts +477 -0
  304. package/examples/condition-patterns/route-skipif-patterns.ts +468 -0
  305. package/examples/condition-patterns/step-skipif-patterns.ts +0 -0
  306. package/examples/condition-patterns/string-only-conditions.ts +296 -0
  307. package/examples/conversation-flows/completion-transitions.ts +48 -7
  308. package/examples/core-concepts/basic-agent.ts +158 -98
  309. package/examples/core-concepts/schema-driven-extraction.ts +43 -16
  310. package/examples/core-concepts/session-management.ts +117 -29
  311. package/examples/integrations/database-integration.ts +6 -6
  312. package/examples/integrations/healthcare-integration.ts +25 -39
  313. package/examples/integrations/search-integration.ts +8 -8
  314. package/examples/integrations/server-session-management.ts +11 -11
  315. package/examples/persistence/database-persistence.ts +15 -15
  316. package/examples/persistence/memory-sessions.ts +6 -6
  317. package/examples/persistence/redis-persistence.ts +7 -9
  318. package/examples/tools/basic-tools.ts +293 -89
  319. package/examples/tools/data-enrichment-tools.ts +189 -79
  320. package/package.json +6 -4
  321. package/src/adapters/PrismaAdapter.ts +3 -2
  322. package/src/adapters/RedisAdapter.ts +3 -3
  323. package/src/core/Agent.ts +152 -46
  324. package/src/core/BatchExecutor.ts +1156 -0
  325. package/src/core/BatchPromptBuilder.ts +275 -0
  326. package/src/core/PromptComposer.ts +53 -16
  327. package/src/core/ResponseEngine.ts +143 -4
  328. package/src/core/ResponseModal.ts +1035 -137
  329. package/src/core/ResponsePipeline.ts +99 -65
  330. package/src/core/Route.ts +262 -34
  331. package/src/core/RoutingEngine.ts +467 -120
  332. package/src/core/SessionManager.ts +39 -7
  333. package/src/core/Step.ts +338 -32
  334. package/src/core/ToolManager.ts +1394 -0
  335. package/src/index.ts +27 -3
  336. package/src/providers/AnthropicProvider.ts +17 -17
  337. package/src/providers/GeminiProvider.ts +129 -60
  338. package/src/providers/OpenAIProvider.ts +18 -18
  339. package/src/providers/OpenRouterProvider.ts +18 -18
  340. package/src/types/agent.ts +12 -4
  341. package/src/types/index.ts +25 -3
  342. package/src/types/route.ts +136 -15
  343. package/src/types/template.ts +70 -2
  344. package/src/types/tool.ts +116 -25
  345. package/src/utils/condition.ts +190 -0
  346. package/src/utils/index.ts +12 -0
  347. package/src/utils/json.ts +46 -0
  348. package/src/utils/retry.ts +5 -4
  349. package/src/utils/template.ts +109 -0
  350. package/dist/cjs/src/adapters/MemoryAdapter.d.ts.map +0 -1
  351. package/dist/cjs/src/adapters/MemoryAdapter.js.map +0 -1
  352. package/dist/cjs/src/adapters/MongoAdapter.d.ts.map +0 -1
  353. package/dist/cjs/src/adapters/MongoAdapter.js.map +0 -1
  354. package/dist/cjs/src/adapters/OpenSearchAdapter.d.ts.map +0 -1
  355. package/dist/cjs/src/adapters/OpenSearchAdapter.js.map +0 -1
  356. package/dist/cjs/src/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  357. package/dist/cjs/src/adapters/PostgreSQLAdapter.js.map +0 -1
  358. package/dist/cjs/src/adapters/PrismaAdapter.d.ts.map +0 -1
  359. package/dist/cjs/src/adapters/PrismaAdapter.js.map +0 -1
  360. package/dist/cjs/src/adapters/RedisAdapter.d.ts.map +0 -1
  361. package/dist/cjs/src/adapters/RedisAdapter.js.map +0 -1
  362. package/dist/cjs/src/adapters/SQLiteAdapter.d.ts.map +0 -1
  363. package/dist/cjs/src/adapters/SQLiteAdapter.js.map +0 -1
  364. package/dist/cjs/src/adapters/index.d.ts.map +0 -1
  365. package/dist/cjs/src/adapters/index.js.map +0 -1
  366. package/dist/cjs/src/constants/index.d.ts.map +0 -1
  367. package/dist/cjs/src/constants/index.js.map +0 -1
  368. package/dist/cjs/src/core/Agent.d.ts.map +0 -1
  369. package/dist/cjs/src/core/Agent.js.map +0 -1
  370. package/dist/cjs/src/core/Events.d.ts.map +0 -1
  371. package/dist/cjs/src/core/Events.js.map +0 -1
  372. package/dist/cjs/src/core/PersistenceManager.d.ts.map +0 -1
  373. package/dist/cjs/src/core/PersistenceManager.js.map +0 -1
  374. package/dist/cjs/src/core/PromptComposer.d.ts.map +0 -1
  375. package/dist/cjs/src/core/PromptComposer.js.map +0 -1
  376. package/dist/cjs/src/core/ResponseEngine.d.ts.map +0 -1
  377. package/dist/cjs/src/core/ResponseEngine.js +0 -84
  378. package/dist/cjs/src/core/ResponseEngine.js.map +0 -1
  379. package/dist/cjs/src/core/ResponseModal.d.ts.map +0 -1
  380. package/dist/cjs/src/core/ResponseModal.js.map +0 -1
  381. package/dist/cjs/src/core/ResponsePipeline.d.ts.map +0 -1
  382. package/dist/cjs/src/core/ResponsePipeline.js.map +0 -1
  383. package/dist/cjs/src/core/Route.d.ts.map +0 -1
  384. package/dist/cjs/src/core/Route.js +0 -343
  385. package/dist/cjs/src/core/Route.js.map +0 -1
  386. package/dist/cjs/src/core/RoutingEngine.d.ts.map +0 -1
  387. package/dist/cjs/src/core/RoutingEngine.js.map +0 -1
  388. package/dist/cjs/src/core/SessionManager.d.ts.map +0 -1
  389. package/dist/cjs/src/core/SessionManager.js.map +0 -1
  390. package/dist/cjs/src/core/Step.d.ts +0 -96
  391. package/dist/cjs/src/core/Step.d.ts.map +0 -1
  392. package/dist/cjs/src/core/Step.js +0 -206
  393. package/dist/cjs/src/core/Step.js.map +0 -1
  394. package/dist/cjs/src/core/ToolExecutor.d.ts +0 -45
  395. package/dist/cjs/src/core/ToolExecutor.d.ts.map +0 -1
  396. package/dist/cjs/src/core/ToolExecutor.js +0 -84
  397. package/dist/cjs/src/core/ToolExecutor.js.map +0 -1
  398. package/dist/cjs/src/index.d.ts.map +0 -1
  399. package/dist/cjs/src/index.js.map +0 -1
  400. package/dist/cjs/src/providers/AnthropicProvider.d.ts.map +0 -1
  401. package/dist/cjs/src/providers/AnthropicProvider.js.map +0 -1
  402. package/dist/cjs/src/providers/GeminiProvider.d.ts.map +0 -1
  403. package/dist/cjs/src/providers/GeminiProvider.js.map +0 -1
  404. package/dist/cjs/src/providers/OpenAIProvider.d.ts.map +0 -1
  405. package/dist/cjs/src/providers/OpenAIProvider.js.map +0 -1
  406. package/dist/cjs/src/providers/OpenRouterProvider.d.ts.map +0 -1
  407. package/dist/cjs/src/providers/OpenRouterProvider.js.map +0 -1
  408. package/dist/cjs/src/providers/index.d.ts.map +0 -1
  409. package/dist/cjs/src/providers/index.js.map +0 -1
  410. package/dist/cjs/src/types/agent.d.ts.map +0 -1
  411. package/dist/cjs/src/types/agent.js.map +0 -1
  412. package/dist/cjs/src/types/ai.d.ts.map +0 -1
  413. package/dist/cjs/src/types/ai.js.map +0 -1
  414. package/dist/cjs/src/types/history.d.ts.map +0 -1
  415. package/dist/cjs/src/types/history.js.map +0 -1
  416. package/dist/cjs/src/types/index.d.ts.map +0 -1
  417. package/dist/cjs/src/types/index.js.map +0 -1
  418. package/dist/cjs/src/types/persistence.d.ts.map +0 -1
  419. package/dist/cjs/src/types/persistence.js.map +0 -1
  420. package/dist/cjs/src/types/route.d.ts.map +0 -1
  421. package/dist/cjs/src/types/routing.d.ts.map +0 -1
  422. package/dist/cjs/src/types/schema.d.ts.map +0 -1
  423. package/dist/cjs/src/types/session.d.ts.map +0 -1
  424. package/dist/cjs/src/types/session.js.map +0 -1
  425. package/dist/cjs/src/types/template.d.ts +0 -30
  426. package/dist/cjs/src/types/template.d.ts.map +0 -1
  427. package/dist/cjs/src/types/tool.d.ts +0 -60
  428. package/dist/cjs/src/types/tool.d.ts.map +0 -1
  429. package/dist/cjs/src/types/tool.js +0 -6
  430. package/dist/cjs/src/types/tool.js.map +0 -1
  431. package/dist/cjs/src/utils/clone.d.ts.map +0 -1
  432. package/dist/cjs/src/utils/clone.js.map +0 -1
  433. package/dist/cjs/src/utils/event.d.ts.map +0 -1
  434. package/dist/cjs/src/utils/event.js.map +0 -1
  435. package/dist/cjs/src/utils/history.d.ts.map +0 -1
  436. package/dist/cjs/src/utils/history.js.map +0 -1
  437. package/dist/cjs/src/utils/id.d.ts.map +0 -1
  438. package/dist/cjs/src/utils/id.js.map +0 -1
  439. package/dist/cjs/src/utils/index.d.ts.map +0 -1
  440. package/dist/cjs/src/utils/index.js.map +0 -1
  441. package/dist/cjs/src/utils/logger.d.ts.map +0 -1
  442. package/dist/cjs/src/utils/logger.js.map +0 -1
  443. package/dist/cjs/src/utils/retry.d.ts.map +0 -1
  444. package/dist/cjs/src/utils/retry.js.map +0 -1
  445. package/dist/cjs/src/utils/session.d.ts.map +0 -1
  446. package/dist/cjs/src/utils/session.js.map +0 -1
  447. package/dist/cjs/src/utils/template.d.ts.map +0 -1
  448. package/dist/cjs/src/utils/template.js.map +0 -1
  449. package/dist/src/adapters/MemoryAdapter.js.map +0 -1
  450. package/dist/src/adapters/MongoAdapter.js.map +0 -1
  451. package/dist/src/adapters/OpenSearchAdapter.js.map +0 -1
  452. package/dist/src/adapters/PostgreSQLAdapter.js.map +0 -1
  453. package/dist/src/adapters/PrismaAdapter.js.map +0 -1
  454. package/dist/src/adapters/RedisAdapter.js.map +0 -1
  455. package/dist/src/adapters/SQLiteAdapter.js.map +0 -1
  456. package/dist/src/adapters/index.js.map +0 -1
  457. package/dist/src/constants/index.js.map +0 -1
  458. package/dist/src/core/Agent.d.ts.map +0 -1
  459. package/dist/src/core/Agent.js.map +0 -1
  460. package/dist/src/core/Events.js.map +0 -1
  461. package/dist/src/core/PersistenceManager.js.map +0 -1
  462. package/dist/src/core/PromptComposer.d.ts.map +0 -1
  463. package/dist/src/core/PromptComposer.js.map +0 -1
  464. package/dist/src/core/ResponseEngine.js +0 -80
  465. package/dist/src/core/ResponseEngine.js.map +0 -1
  466. package/dist/src/core/ResponseModal.d.ts.map +0 -1
  467. package/dist/src/core/ResponseModal.js.map +0 -1
  468. package/dist/src/core/ResponsePipeline.d.ts.map +0 -1
  469. package/dist/src/core/ResponsePipeline.js.map +0 -1
  470. package/dist/src/core/Route.d.ts.map +0 -1
  471. package/dist/src/core/Route.js +0 -339
  472. package/dist/src/core/Route.js.map +0 -1
  473. package/dist/src/core/RoutingEngine.d.ts.map +0 -1
  474. package/dist/src/core/RoutingEngine.js.map +0 -1
  475. package/dist/src/core/SessionManager.d.ts.map +0 -1
  476. package/dist/src/core/SessionManager.js.map +0 -1
  477. package/dist/src/core/Step.d.ts +0 -96
  478. package/dist/src/core/Step.d.ts.map +0 -1
  479. package/dist/src/core/Step.js +0 -202
  480. package/dist/src/core/Step.js.map +0 -1
  481. package/dist/src/core/ToolExecutor.d.ts +0 -45
  482. package/dist/src/core/ToolExecutor.d.ts.map +0 -1
  483. package/dist/src/core/ToolExecutor.js +0 -80
  484. package/dist/src/core/ToolExecutor.js.map +0 -1
  485. package/dist/src/index.d.ts.map +0 -1
  486. package/dist/src/index.js.map +0 -1
  487. package/dist/src/providers/AnthropicProvider.js.map +0 -1
  488. package/dist/src/providers/GeminiProvider.js.map +0 -1
  489. package/dist/src/providers/OpenAIProvider.js.map +0 -1
  490. package/dist/src/providers/OpenRouterProvider.js.map +0 -1
  491. package/dist/src/providers/index.js.map +0 -1
  492. package/dist/src/types/agent.d.ts.map +0 -1
  493. package/dist/src/types/agent.js.map +0 -1
  494. package/dist/src/types/history.js.map +0 -1
  495. package/dist/src/types/index.js.map +0 -1
  496. package/dist/src/types/persistence.js.map +0 -1
  497. package/dist/src/types/route.d.ts.map +0 -1
  498. package/dist/src/types/template.d.ts +0 -30
  499. package/dist/src/types/template.d.ts.map +0 -1
  500. package/dist/src/types/tool.d.ts +0 -60
  501. package/dist/src/types/tool.d.ts.map +0 -1
  502. package/dist/src/types/tool.js +0 -5
  503. package/dist/src/types/tool.js.map +0 -1
  504. package/dist/src/utils/clone.js.map +0 -1
  505. package/dist/src/utils/event.js.map +0 -1
  506. package/dist/src/utils/history.js.map +0 -1
  507. package/dist/src/utils/id.js.map +0 -1
  508. package/dist/src/utils/index.d.ts.map +0 -1
  509. package/dist/src/utils/index.js.map +0 -1
  510. package/dist/src/utils/logger.js.map +0 -1
  511. package/dist/src/utils/retry.d.ts.map +0 -1
  512. package/dist/src/utils/retry.js.map +0 -1
  513. package/dist/src/utils/session.js.map +0 -1
  514. package/dist/src/utils/template.d.ts.map +0 -1
  515. package/dist/src/utils/template.js.map +0 -1
  516. package/docs/core/tools/tool-execution.md +0 -815
  517. package/src/core/ToolExecutor.ts +0 -126
  518. /package/dist/{cjs/src/adapters → adapters}/MemoryAdapter.d.ts +0 -0
  519. /package/dist/{src/adapters → adapters}/MemoryAdapter.js +0 -0
  520. /package/dist/{cjs/src/adapters → adapters}/MongoAdapter.d.ts +0 -0
  521. /package/dist/{src/adapters → adapters}/MongoAdapter.js +0 -0
  522. /package/dist/{cjs/src/adapters → adapters}/OpenSearchAdapter.d.ts +0 -0
  523. /package/dist/{src/adapters → adapters}/OpenSearchAdapter.js +0 -0
  524. /package/dist/{cjs/src/adapters → adapters}/PostgreSQLAdapter.d.ts +0 -0
  525. /package/dist/{src/adapters → adapters}/PostgreSQLAdapter.js +0 -0
  526. /package/dist/{cjs/src/adapters → adapters}/PrismaAdapter.d.ts +0 -0
  527. /package/dist/{cjs/src/adapters → adapters}/RedisAdapter.d.ts +0 -0
  528. /package/dist/{cjs/src/adapters → adapters}/SQLiteAdapter.d.ts +0 -0
  529. /package/dist/{src/adapters → adapters}/SQLiteAdapter.js +0 -0
  530. /package/dist/{cjs/src/adapters → adapters}/index.d.ts +0 -0
  531. /package/dist/{src/adapters → adapters}/index.js +0 -0
  532. /package/dist/{src → cjs}/adapters/MemoryAdapter.d.ts +0 -0
  533. /package/dist/{src → cjs}/adapters/MemoryAdapter.d.ts.map +0 -0
  534. /package/dist/cjs/{src/adapters → adapters}/MemoryAdapter.js +0 -0
  535. /package/dist/{src → cjs}/adapters/MongoAdapter.d.ts +0 -0
  536. /package/dist/{src → cjs}/adapters/MongoAdapter.d.ts.map +0 -0
  537. /package/dist/cjs/{src/adapters → adapters}/MongoAdapter.js +0 -0
  538. /package/dist/{src → cjs}/adapters/OpenSearchAdapter.d.ts +0 -0
  539. /package/dist/{src → cjs}/adapters/OpenSearchAdapter.d.ts.map +0 -0
  540. /package/dist/cjs/{src/adapters → adapters}/OpenSearchAdapter.js +0 -0
  541. /package/dist/{src → cjs}/adapters/PostgreSQLAdapter.d.ts +0 -0
  542. /package/dist/{src → cjs}/adapters/PostgreSQLAdapter.d.ts.map +0 -0
  543. /package/dist/cjs/{src/adapters → adapters}/PostgreSQLAdapter.js +0 -0
  544. /package/dist/{src → cjs}/adapters/PrismaAdapter.d.ts +0 -0
  545. /package/dist/{src → cjs}/adapters/RedisAdapter.d.ts +0 -0
  546. /package/dist/{src → cjs}/adapters/RedisAdapter.d.ts.map +0 -0
  547. /package/dist/{src → cjs}/adapters/SQLiteAdapter.d.ts +0 -0
  548. /package/dist/{src → cjs}/adapters/SQLiteAdapter.d.ts.map +0 -0
  549. /package/dist/cjs/{src/adapters → adapters}/SQLiteAdapter.js +0 -0
  550. /package/dist/{src → cjs}/adapters/index.d.ts +0 -0
  551. /package/dist/{src → cjs}/adapters/index.d.ts.map +0 -0
  552. /package/dist/cjs/{src/adapters → adapters}/index.js +0 -0
  553. /package/dist/cjs/{src/constants → constants}/index.d.ts +0 -0
  554. /package/dist/{src → cjs}/constants/index.d.ts.map +0 -0
  555. /package/dist/cjs/{src/constants → constants}/index.js +0 -0
  556. /package/dist/cjs/{src/core → core}/Events.d.ts +0 -0
  557. /package/dist/{src → cjs}/core/Events.d.ts.map +0 -0
  558. /package/dist/cjs/{src/core → core}/Events.js +0 -0
  559. /package/dist/cjs/{src/core → core}/PersistenceManager.d.ts +0 -0
  560. /package/dist/{src → cjs}/core/PersistenceManager.d.ts.map +0 -0
  561. /package/dist/cjs/{src/core → core}/PersistenceManager.js +0 -0
  562. /package/dist/cjs/{src/core → core}/ResponseEngine.d.ts +0 -0
  563. /package/dist/cjs/{src/providers → providers}/AnthropicProvider.d.ts +0 -0
  564. /package/dist/{src → cjs}/providers/AnthropicProvider.d.ts.map +0 -0
  565. /package/dist/cjs/{src/providers → providers}/GeminiProvider.d.ts +0 -0
  566. /package/dist/cjs/{src/providers → providers}/OpenAIProvider.d.ts +0 -0
  567. /package/dist/{src → cjs}/providers/OpenAIProvider.d.ts.map +0 -0
  568. /package/dist/cjs/{src/providers → providers}/OpenRouterProvider.d.ts +0 -0
  569. /package/dist/{src → cjs}/providers/OpenRouterProvider.d.ts.map +0 -0
  570. /package/dist/cjs/{src/providers → providers}/index.d.ts +0 -0
  571. /package/dist/{src → cjs}/providers/index.d.ts.map +0 -0
  572. /package/dist/cjs/{src/providers → providers}/index.js +0 -0
  573. /package/dist/cjs/{src/types → types}/agent.js +0 -0
  574. /package/dist/cjs/{src/types → types}/ai.d.ts +0 -0
  575. /package/dist/{src → cjs}/types/ai.d.ts.map +0 -0
  576. /package/dist/cjs/{src/types → types}/ai.js +0 -0
  577. /package/dist/cjs/{src/types → types}/history.d.ts +0 -0
  578. /package/dist/{src → cjs}/types/history.d.ts.map +0 -0
  579. /package/dist/cjs/{src/types → types}/history.js +0 -0
  580. /package/dist/cjs/{src/types → types}/persistence.d.ts +0 -0
  581. /package/dist/{src → cjs}/types/persistence.d.ts.map +0 -0
  582. /package/dist/cjs/{src/types → types}/persistence.js +0 -0
  583. /package/dist/cjs/{src/types → types}/route.js +0 -0
  584. /package/dist/cjs/{src/types → types}/routing.d.ts +0 -0
  585. /package/dist/{src → cjs}/types/routing.d.ts.map +0 -0
  586. /package/dist/cjs/{src/types → types}/routing.js +0 -0
  587. /package/dist/{src → cjs}/types/routing.js.map +0 -0
  588. /package/dist/cjs/{src/types → types}/schema.d.ts +0 -0
  589. /package/dist/{src → cjs}/types/schema.d.ts.map +0 -0
  590. /package/dist/cjs/{src/types → types}/schema.js +0 -0
  591. /package/dist/{src → cjs}/types/schema.js.map +0 -0
  592. /package/dist/cjs/{src/types → types}/session.d.ts +0 -0
  593. /package/dist/{src → cjs}/types/session.d.ts.map +0 -0
  594. /package/dist/cjs/{src/types → types}/session.js +0 -0
  595. /package/dist/cjs/{src/types → types}/template.js +0 -0
  596. /package/dist/{src → cjs}/types/template.js.map +0 -0
  597. /package/dist/cjs/{src/utils → utils}/clone.d.ts +0 -0
  598. /package/dist/{src → cjs}/utils/clone.d.ts.map +0 -0
  599. /package/dist/cjs/{src/utils → utils}/clone.js +0 -0
  600. /package/dist/cjs/{src/utils → utils}/event.d.ts +0 -0
  601. /package/dist/{src → cjs}/utils/event.d.ts.map +0 -0
  602. /package/dist/cjs/{src/utils → utils}/event.js +0 -0
  603. /package/dist/cjs/{src/utils → utils}/history.d.ts +0 -0
  604. /package/dist/{src → cjs}/utils/history.d.ts.map +0 -0
  605. /package/dist/cjs/{src/utils → utils}/history.js +0 -0
  606. /package/dist/cjs/{src/utils → utils}/id.d.ts +0 -0
  607. /package/dist/{src → cjs}/utils/id.d.ts.map +0 -0
  608. /package/dist/cjs/{src/utils → utils}/id.js +0 -0
  609. /package/dist/cjs/{src/utils → utils}/logger.d.ts +0 -0
  610. /package/dist/{src → cjs}/utils/logger.d.ts.map +0 -0
  611. /package/dist/cjs/{src/utils → utils}/logger.js +0 -0
  612. /package/dist/cjs/{src/utils → utils}/session.d.ts +0 -0
  613. /package/dist/{src → cjs}/utils/session.d.ts.map +0 -0
  614. /package/dist/cjs/{src/utils → utils}/session.js +0 -0
  615. /package/dist/{src/constants → constants}/index.d.ts +0 -0
  616. /package/dist/{src/constants → constants}/index.js +0 -0
  617. /package/dist/{src/core → core}/Events.d.ts +0 -0
  618. /package/dist/{src/core → core}/Events.js +0 -0
  619. /package/dist/{src/core → core}/PersistenceManager.d.ts +0 -0
  620. /package/dist/{src/core → core}/PersistenceManager.js +0 -0
  621. /package/dist/{src/core → core}/ResponseEngine.d.ts +0 -0
  622. /package/dist/{src/providers → providers}/AnthropicProvider.d.ts +0 -0
  623. /package/dist/{src/providers → providers}/GeminiProvider.d.ts +0 -0
  624. /package/dist/{src/providers → providers}/OpenAIProvider.d.ts +0 -0
  625. /package/dist/{src/providers → providers}/OpenRouterProvider.d.ts +0 -0
  626. /package/dist/{src/providers → providers}/index.d.ts +0 -0
  627. /package/dist/{src/providers → providers}/index.js +0 -0
  628. /package/dist/{src/types → types}/agent.js +0 -0
  629. /package/dist/{src/types → types}/ai.d.ts +0 -0
  630. /package/dist/{src/types → types}/ai.js +0 -0
  631. /package/dist/{src/types → types}/history.d.ts +0 -0
  632. /package/dist/{src/types → types}/history.js +0 -0
  633. /package/dist/{src/types → types}/persistence.d.ts +0 -0
  634. /package/dist/{src/types → types}/persistence.js +0 -0
  635. /package/dist/{src/types → types}/route.js +0 -0
  636. /package/dist/{src/types → types}/routing.d.ts +0 -0
  637. /package/dist/{src/types → types}/routing.js +0 -0
  638. /package/dist/{src/types → types}/schema.d.ts +0 -0
  639. /package/dist/{src/types → types}/schema.js +0 -0
  640. /package/dist/{src/types → types}/session.d.ts +0 -0
  641. /package/dist/{src/types → types}/session.js +0 -0
  642. /package/dist/{src/types → types}/template.js +0 -0
  643. /package/dist/{src/utils → utils}/clone.d.ts +0 -0
  644. /package/dist/{src/utils → utils}/clone.js +0 -0
  645. /package/dist/{src/utils → utils}/event.d.ts +0 -0
  646. /package/dist/{src/utils → utils}/event.js +0 -0
  647. /package/dist/{src/utils → utils}/history.d.ts +0 -0
  648. /package/dist/{src/utils → utils}/history.js +0 -0
  649. /package/dist/{src/utils → utils}/id.d.ts +0 -0
  650. /package/dist/{src/utils → utils}/id.js +0 -0
  651. /package/dist/{src/utils → utils}/logger.d.ts +0 -0
  652. /package/dist/{src/utils → utils}/logger.js +0 -0
  653. /package/dist/{src/utils → utils}/session.d.ts +0 -0
  654. /package/dist/{src/utils → utils}/session.js +0 -0
@@ -15,16 +15,21 @@ import type {
15
15
  ToolEventData,
16
16
  AgentStructuredResponse,
17
17
  Term,
18
+ StoppedReason,
18
19
  } from "../types";
19
20
  import { EventKind, MessageRole } from "../types";
20
21
  import type { Agent } from "./Agent";
21
22
  import type { Route } from "./Route";
22
- import { Step } from "./Step";
23
+ import { Step } from "./Step";
23
24
  import { ResponseEngine } from "./ResponseEngine";
24
25
  import { ResponsePipeline } from "./ResponsePipeline";
26
+ import { BatchExecutor, type HookFunction } from "./BatchExecutor";
27
+ import { BatchPromptBuilder } from "./BatchPromptBuilder";
25
28
  import { cloneDeep, mergeCollected, enterStep, getLastMessageFromHistory, render, logger, historyToEvents } from "../utils";
26
- import { ToolExecutor } from "./ToolExecutor";
29
+ import { createTemplateContext } from "../utils/template";
30
+ import type { ToolManager } from "./ToolManager";
27
31
  import { END_ROUTE_ID } from "../constants";
32
+ import type { StepOptions } from "../types/route";
28
33
 
29
34
  /**
30
35
  * Configuration options for ResponseModal
@@ -98,9 +103,9 @@ export class ResponseGenerationError extends Error {
98
103
  * Create a ResponseGenerationError from an unknown error
99
104
  */
100
105
  static fromError(
101
- error: unknown,
102
- phase: string,
103
- params?: Record<string, unknown>,
106
+ error: unknown,
107
+ phase: string,
108
+ params?: Record<string, unknown>,
104
109
  context?: Record<string, unknown>
105
110
  ): ResponseGenerationError {
106
111
  const message = error instanceof Error ? error.message : String(error);
@@ -129,6 +134,12 @@ interface ResponseContext<TContext = unknown, TData = unknown> {
129
134
  selectedStep?: Step<TContext, TData>;
130
135
  responseDirectives?: string[];
131
136
  isRouteComplete: boolean;
137
+ /** Batch of steps to execute (for multi-step execution) */
138
+ batchSteps?: StepOptions<TContext, TData>[];
139
+ /** Reason why batch determination stopped */
140
+ batchStoppedReason?: StoppedReason;
141
+ /** Step that caused batch to stop (if applicable) */
142
+ batchStoppedAtStep?: StepOptions<TContext, TData>;
132
143
  }
133
144
 
134
145
  /**
@@ -138,6 +149,8 @@ interface ResponseContext<TContext = unknown, TData = unknown> {
138
149
  export class ResponseModal<TContext = unknown, TData = unknown> {
139
150
  private readonly responseEngine: ResponseEngine<TContext, TData>;
140
151
  private readonly responsePipeline: ResponsePipeline<TContext, TData>;
152
+ private readonly batchExecutor: BatchExecutor<TContext, TData>;
153
+ private readonly batchPromptBuilder: BatchPromptBuilder<TContext, TData>;
141
154
 
142
155
  constructor(
143
156
  private readonly agent: Agent<TContext, TData>,
@@ -149,13 +162,20 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
149
162
  // Initialize response pipeline with agent dependencies
150
163
  this.responsePipeline = new ResponsePipeline<TContext, TData>(
151
164
  this.agent.getAgentOptions(),
152
- this.agent.getRoutes(),
165
+ () => this.agent.getRoutes(), // Pass a function to get routes dynamically
153
166
  this.agent.getTools(),
154
167
  this.agent.getRoutingEngine(),
155
168
  this.agent.updateContext.bind(this.agent),
156
169
  this.agent.getUpdateDataMethod(),
157
- this.agent.updateCollectedData.bind(this.agent)
170
+ this.agent.updateCollectedData.bind(this.agent),
171
+ this.getToolManager()
158
172
  );
173
+
174
+ // Initialize batch executor for multi-step execution
175
+ this.batchExecutor = new BatchExecutor<TContext, TData>();
176
+
177
+ // Initialize batch prompt builder for combined prompts
178
+ this.batchPromptBuilder = new BatchPromptBuilder<TContext, TData>();
159
179
  }
160
180
 
161
181
  /**
@@ -165,7 +185,6 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
165
185
  try {
166
186
  // Use unified response preparation and routing
167
187
  const responseContext = await this.prepareUnifiedResponseContext(params);
168
-
169
188
  // Generate response using unified logic
170
189
  const result = await this.generateUnifiedResponse(responseContext);
171
190
 
@@ -330,6 +349,21 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
330
349
  return this.responsePipeline;
331
350
  }
332
351
 
352
+ /**
353
+ * Get the ToolManager instance from the agent
354
+ * @private
355
+ */
356
+ private getToolManager(): ToolManager<TContext, TData> | undefined {
357
+ // Check if agent has a tool property (ToolManager)
358
+ if (this.agent && 'tool' in this.agent && this.agent.tool) {
359
+ return this.agent.tool;
360
+ }
361
+
362
+ // Log warning if ToolManager is not available
363
+ logger.warn(`[ResponseModal] ToolManager not available on agent - tool execution will use fallback methods`);
364
+ return undefined;
365
+ }
366
+
333
367
  // UNIFIED RESPONSE LOGIC - Consolidates common logic between streaming and non-streaming
334
368
  // ============================================================================
335
369
 
@@ -403,12 +437,16 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
403
437
  }
404
438
 
405
439
  // PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use
440
+ // Also performs pre-extraction and batch determination
406
441
  let routingResult: {
407
442
  selectedRoute?: Route<TContext, TData>;
408
443
  selectedStep?: Step<TContext, TData>;
409
- responseDirectives?: string[];
444
+ responseDirectives?: string[];
410
445
  session: SessionState<TData>;
411
446
  isRouteComplete: boolean;
447
+ batchSteps?: StepOptions<TContext, TData>[];
448
+ batchStoppedReason?: StoppedReason;
449
+ batchStoppedAtStep?: StepOptions<TContext, TData>;
412
450
  };
413
451
  try {
414
452
  routingResult = await this.handleUnifiedRoutingAndStepSelection({
@@ -429,6 +467,9 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
429
467
  selectedStep: routingResult.selectedStep,
430
468
  responseDirectives: routingResult.responseDirectives,
431
469
  isRouteComplete: routingResult.isRouteComplete,
470
+ batchSteps: routingResult.batchSteps,
471
+ batchStoppedReason: routingResult.batchStoppedReason,
472
+ batchStoppedAtStep: routingResult.batchStoppedAtStep,
432
473
  };
433
474
  } catch (error) {
434
475
  // Re-throw ResponseGenerationError as-is, wrap others
@@ -454,6 +495,12 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
454
495
  responseDirectives?: string[];
455
496
  session: SessionState<TData>;
456
497
  isRouteComplete: boolean;
498
+ /** Batch of steps to execute (for multi-step execution) */
499
+ batchSteps?: StepOptions<TContext, TData>[];
500
+ /** Reason why batch determination stopped */
501
+ batchStoppedReason?: StoppedReason;
502
+ /** Step that caused batch to stop (if applicable) */
503
+ batchStoppedAtStep?: StepOptions<TContext, TData>;
457
504
  }> {
458
505
  try {
459
506
  // Use the ResponsePipeline for optimized routing and step selection
@@ -466,12 +513,83 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
466
513
  signal: params.signal,
467
514
  });
468
515
 
516
+ let updatedSession = routingResult.session;
517
+ let isRouteComplete = routingResult.isRouteComplete;
518
+
519
+ // PRE-EXTRACTION: If entering a route that collects data, extract data from user message first
520
+ // This allows us to skip steps whose data is already provided
521
+ // Requirement 3.1: Perform Pre_Extraction before determining the Batch
522
+ if (routingResult.selectedRoute && !isRouteComplete) {
523
+ // Always pre-extract when route collects data (not just on new route entry)
524
+ // This ensures batch determination has the most up-to-date data
525
+ if (this.shouldPreExtractData(routingResult.selectedRoute)) {
526
+ logger.debug(
527
+ `[ResponseModal] Pre-extracting data for route: ${routingResult.selectedRoute.title}`
528
+ );
529
+
530
+ const extractedData = await this.preExtractRouteData({
531
+ route: routingResult.selectedRoute,
532
+ history: params.history,
533
+ context: params.context,
534
+ session: updatedSession,
535
+ signal: params.signal,
536
+ });
537
+
538
+ if (extractedData && Object.keys(extractedData).length > 0) {
539
+ logger.debug(
540
+ `[ResponseModal] Pre-extracted data:`,
541
+ extractedData
542
+ );
543
+ // Requirement 3.3: Merge pre-extracted data into session before batch determination
544
+ updatedSession = mergeCollected(updatedSession, extractedData);
545
+ // Also update agent's collected data
546
+ await this.agent.updateCollectedData(extractedData);
547
+
548
+ // Re-check route completion after pre-extraction
549
+ const allRequiredFieldsCollected = routingResult.selectedRoute.isComplete(updatedSession.data || {});
550
+ if (allRequiredFieldsCollected) {
551
+ logger.debug(
552
+ `[ResponseModal] Route ${routingResult.selectedRoute.title} completed after pre-extraction`
553
+ );
554
+ isRouteComplete = true;
555
+ }
556
+ }
557
+ }
558
+ }
559
+
560
+ // BATCH DETERMINATION: Use BatchExecutor to determine which steps can execute together
561
+ // Requirement 3.4: Pre-extraction results affect batch determination
562
+ let batchSteps: StepOptions<TContext, TData>[] | undefined;
563
+ let batchStoppedReason: StoppedReason | undefined;
564
+ let batchStoppedAtStep: StepOptions<TContext, TData> | undefined;
565
+
566
+ if (routingResult.selectedRoute && !isRouteComplete) {
567
+ // Determine current step position for batch determination
568
+ const currentStep = routingResult.selectedStep ||
569
+ (updatedSession.currentStep ? routingResult.selectedRoute.getStep(updatedSession.currentStep.id) : undefined);
570
+
571
+ logger.debug(`[ResponseModal] Determining batch starting from step: ${currentStep?.id || 'initial'}`);
572
+
573
+ const batchResult = await this.batchExecutor.determineBatch({
574
+ route: routingResult.selectedRoute,
575
+ currentStep,
576
+ sessionData: updatedSession.data || {},
577
+ context: params.context,
578
+ });
579
+
580
+ batchSteps = batchResult.steps;
581
+ batchStoppedReason = batchResult.stoppedReason;
582
+ batchStoppedAtStep = batchResult.stoppedAtStep;
583
+
584
+ logger.debug(`[ResponseModal] Batch determined: ${batchSteps.length} steps, stopped reason: ${batchStoppedReason}`);
585
+ }
586
+
469
587
  // Determine next step using pipeline method for consistency
470
- const stepResult = this.responsePipeline.determineNextStep({
588
+ const stepResult = await this.responsePipeline.determineNextStep({
471
589
  selectedRoute: routingResult.selectedRoute,
472
590
  selectedStep: routingResult.selectedStep,
473
- session: routingResult.session,
474
- isRouteComplete: routingResult.isRouteComplete,
591
+ session: updatedSession, // Use updated session with pre-extracted data
592
+ isRouteComplete, // Use updated completion status
475
593
  });
476
594
 
477
595
  return {
@@ -479,13 +597,106 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
479
597
  selectedStep: stepResult.nextStep, // Use the determined next step
480
598
  responseDirectives: routingResult.responseDirectives,
481
599
  session: stepResult.session,
482
- isRouteComplete: routingResult.isRouteComplete,
600
+ isRouteComplete, // Use updated completion status
601
+ batchSteps,
602
+ batchStoppedReason,
603
+ batchStoppedAtStep,
483
604
  };
484
605
  } catch (error) {
485
606
  throw ResponseGenerationError.fromError(error, 'routing_optimization', params);
486
607
  }
487
608
  }
488
609
 
610
+ /**
611
+ * Check if a route should pre-extract data before determining the initial step
612
+ * @private
613
+ */
614
+ private shouldPreExtractData(route: Route<TContext, TData>): boolean {
615
+ // Pre-extract if route has declared required or optional fields
616
+ if (route.requiredFields && route.requiredFields.length > 0) {
617
+ return true;
618
+ }
619
+ if (route.optionalFields && route.optionalFields.length > 0) {
620
+ return true;
621
+ }
622
+
623
+ // Pre-extract if any step in the route collects data
624
+ const steps = route.getAllSteps();
625
+ const hasDataCollectionSteps = steps.some(
626
+ step => step.collect && step.collect.length > 0
627
+ );
628
+
629
+ return hasDataCollectionSteps;
630
+ }
631
+
632
+ /**
633
+ * Pre-extract data from user message when entering a route
634
+ * This allows skipping steps whose data is already provided
635
+ * @private
636
+ */
637
+ private async preExtractRouteData(params: {
638
+ route: Route<TContext, TData>;
639
+ history: Event[];
640
+ context: TContext;
641
+ session: SessionState<TData>;
642
+ signal?: AbortSignal;
643
+ }): Promise<Partial<TData>> {
644
+ const { route, history, context, signal } = params;
645
+
646
+ // Build a schema for data extraction based on route's fields
647
+ const extractionSchema = this.agent.getSchema();
648
+ if (!extractionSchema) {
649
+ logger.warn(`[ResponseModal] No schema available for pre-extraction`);
650
+ return {};
651
+ }
652
+
653
+ // Get last user message
654
+ const lastMessage = getLastMessageFromHistory(history);
655
+
656
+ // Build extraction prompt
657
+ const extractionPrompt = [
658
+ `Extract any relevant information from the user's message that matches the following data fields.`,
659
+ `Only extract information that is explicitly stated or clearly implied.`,
660
+ ``,
661
+ `User's message: "${lastMessage}"`,
662
+ ``,
663
+ `Extract data for these fields if present:`,
664
+ ];
665
+
666
+ // Add field descriptions
667
+ if (route.requiredFields) {
668
+ extractionPrompt.push(`Required fields: ${route.requiredFields.join(', ')}`);
669
+ }
670
+ if (route.optionalFields) {
671
+ extractionPrompt.push(`Optional fields: ${route.optionalFields.join(', ')}`);
672
+ }
673
+
674
+ extractionPrompt.push(
675
+ ``,
676
+ `Return ONLY the extracted data as JSON. If no data can be extracted, return an empty object {}.`
677
+ );
678
+
679
+ // Call AI to extract data
680
+ const agentOptions = this.agent.getAgentOptions();
681
+ try {
682
+ const result = await agentOptions.provider.generateMessage<TContext, Partial<TData>>({
683
+ prompt: extractionPrompt.join('\n'),
684
+ history,
685
+ context,
686
+ signal,
687
+ parameters: {
688
+ jsonSchema: extractionSchema,
689
+ schemaName: 'data_extraction',
690
+ },
691
+ });
692
+
693
+ return result.structured || {};
694
+ } catch (error) {
695
+ logger.error(`[ResponseModal] Pre-extraction failed:`, error);
696
+ return {};
697
+ }
698
+ }
699
+
489
700
  /**
490
701
  * Unified response generation for non-streaming responses
491
702
  * @private
@@ -493,7 +704,17 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
493
704
  private async generateUnifiedResponse(
494
705
  responseContext: ResponseContext<TContext, TData>
495
706
  ): Promise<AgentResponse<TData>> {
496
- const { effectiveContext, session: initialSession, history, selectedRoute, selectedStep, responseDirectives, isRouteComplete } = responseContext;
707
+ const {
708
+ effectiveContext,
709
+ session: initialSession,
710
+ history,
711
+ selectedRoute,
712
+ selectedStep,
713
+ responseDirectives,
714
+ isRouteComplete,
715
+ batchSteps,
716
+ batchStoppedReason,
717
+ } = responseContext;
497
718
  let session = initialSession;
498
719
 
499
720
  // Get last user message (needed for both route and completion handling)
@@ -503,56 +724,381 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
503
724
 
504
725
  let message: string;
505
726
  let toolCalls: Array<{ toolName: string; arguments: Record<string, unknown> }> | undefined = undefined;
727
+ let executedSteps: StepRef[] | undefined;
728
+ let stoppedReason: StoppedReason | undefined;
506
729
 
730
+
731
+
507
732
  if (selectedRoute && !isRouteComplete) {
508
- // Handle normal route processing
509
- const result = await this.processRouteResponse({
510
- selectedRoute,
511
- selectedStep,
512
- responseDirectives,
513
- session,
514
- history,
515
- context: effectiveContext,
516
- lastMessageText,
517
- historyEvents,
518
- signal: responseContext.history ? undefined : undefined, // TODO: Fix signal passing
519
- });
733
+ // Check if we have batch steps to execute
734
+ if (batchSteps && batchSteps.length > 0) {
735
+ // BATCH EXECUTION: Execute multiple steps in a single LLM call
736
+ logger.debug(`[ResponseModal] Executing batch of ${batchSteps.length} steps`);
520
737
 
521
- message = result.message;
522
- toolCalls = result.toolCalls;
523
- session = result.session;
738
+ const batchResult = await this.executeBatchResponse({
739
+ selectedRoute,
740
+ batchSteps,
741
+ responseDirectives,
742
+ session,
743
+ history,
744
+ context: effectiveContext,
745
+ historyEvents,
746
+ });
747
+
748
+ message = batchResult.message;
749
+ toolCalls = batchResult.toolCalls;
750
+ session = batchResult.session;
751
+ executedSteps = batchResult.executedSteps;
752
+ stoppedReason = batchStoppedReason;
753
+
754
+ } else {
755
+ // SINGLE STEP EXECUTION: Fall back to single-step processing
756
+ // This happens when batch determination returns empty (first step needs input)
757
+ const result = await this.processRouteResponse({
758
+ selectedRoute,
759
+ selectedStep,
760
+ responseDirectives,
761
+ session,
762
+ history,
763
+ context: effectiveContext,
764
+ lastMessageText,
765
+ historyEvents,
766
+ signal: undefined,
767
+ });
768
+
769
+ message = result.message;
770
+ toolCalls = result.toolCalls;
771
+ session = result.session;
772
+
773
+ // Track executed step for single-step execution
774
+ if (selectedStep) {
775
+ executedSteps = [{
776
+ id: selectedStep.id,
777
+ routeId: selectedRoute.id,
778
+ }];
779
+ }
780
+ stoppedReason = batchStoppedReason || 'needs_input';
781
+ }
524
782
 
525
783
  } else if (isRouteComplete && selectedRoute) {
526
784
  // Handle route completion
527
- message = await this.handleRouteCompletion({
528
- selectedRoute,
529
- session,
530
- context: effectiveContext,
531
- lastMessageText,
532
- historyEvents,
533
- });
785
+ logger.debug(`[ResponseModal] Generating completion message for route: ${selectedRoute.title}`);
534
786
 
535
- // Set step to END_ROUTE marker
536
- session = enterStep(session, END_ROUTE_ID, "Route completed");
537
- logger.debug(`[ResponseModal] Route ${selectedRoute.title} completed. Entered END_ROUTE step.`);
787
+ try {
788
+ message = await this.handleRouteCompletion({
789
+ selectedRoute,
790
+ session,
791
+ context: effectiveContext,
792
+ lastMessageText,
793
+ historyEvents,
794
+ signal: undefined,
795
+ });
796
+
797
+ // Set step to END_ROUTE marker
798
+ session = enterStep(session, END_ROUTE_ID, "Route completed");
799
+ stoppedReason = 'route_complete';
800
+ logger.debug(`[ResponseModal] Route ${selectedRoute.title} completed. Entered END_ROUTE step.`);
801
+ } catch (error) {
802
+ logger.error(`[ResponseModal] Error generating completion message:`, error);
803
+ // Fallback to simple completion message
804
+ message = `Thank you! I've recorded all the information for your ${selectedRoute.title.toLowerCase()}.`;
805
+ session = enterStep(session, END_ROUTE_ID, "Route completed");
806
+ stoppedReason = 'route_complete';
807
+ }
538
808
 
539
809
  } else {
540
810
  // Fallback: No routes defined, generate a simple response
811
+
541
812
  message = await this.generateFallbackResponse({
542
813
  history: historyEvents, // Use Event[] for fallback response
543
814
  context: effectiveContext,
544
815
  session,
545
816
  });
817
+
818
+ // For fallback responses, set empty executedSteps and no stoppedReason
819
+ // since there's no route/step execution happening
820
+ executedSteps = [];
821
+ stoppedReason = undefined;
546
822
  }
547
823
 
824
+ // Ensure response structure completeness (Requirement 8.1, 8.2, 8.3)
825
+ // - executedSteps: array of steps executed (empty array if none)
826
+ // - stoppedReason: why execution stopped (undefined for fallback)
827
+ // - session.currentStep: reflects final step position
548
828
  return {
549
829
  message,
550
830
  session,
551
831
  toolCalls,
552
832
  isRouteComplete,
833
+ executedSteps: executedSteps || [],
834
+ stoppedReason,
835
+ };
836
+ }
837
+
838
+ /**
839
+ * Execute a batch of steps with a single LLM call
840
+ *
841
+ * This method:
842
+ * 1. Executes all prepare hooks for steps in the batch (in order)
843
+ * 2. Builds a combined prompt using BatchPromptBuilder
844
+ * 3. Makes a single LLM call
845
+ * 4. Collects data from the response for all steps
846
+ * 5. Executes all finalize hooks for steps in the batch (in order)
847
+ *
848
+ * @private
849
+ * **Validates: Requirements 1.1, 4.4, 5.1, 5.2**
850
+ */
851
+ private async executeBatchResponse(params: {
852
+ selectedRoute: Route<TContext, TData>;
853
+ batchSteps: StepOptions<TContext, TData>[];
854
+ responseDirectives?: string[];
855
+ session: SessionState<TData>;
856
+ history: HistoryItem[];
857
+ context: TContext;
858
+ historyEvents: Event[];
859
+ signal?: AbortSignal;
860
+ }): Promise<{
861
+ message: string;
862
+ toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
863
+ session: SessionState<TData>;
864
+ executedSteps: StepRef[];
865
+ }> {
866
+ const { selectedRoute, batchSteps, history, context, historyEvents, signal } = params;
867
+ let session = params.session;
868
+
869
+ logger.debug(`[ResponseModal] Starting batch execution for ${batchSteps.length} steps`);
870
+
871
+ // Create hook executor function
872
+ const executeHook = async (
873
+ hook: HookFunction<TContext, TData>,
874
+ hookContext: TContext,
875
+ data?: Partial<TData>,
876
+ step?: StepOptions<TContext, TData>
877
+ ): Promise<void> => {
878
+ // Find the route for this step
879
+ const route = selectedRoute;
880
+ // Convert StepOptions to Step if needed for executePrepareFinalize
881
+ const stepInstance = step?.id ? route.getStep(step.id) : undefined;
882
+ await this.executePrepareFinalize(hook, hookContext, data, route, stepInstance);
883
+ };
884
+
885
+ // PHASE 1: Execute all prepare hooks (Requirement 5.1)
886
+ logger.debug(`[ResponseModal] Executing prepare hooks for batch`);
887
+ const prepareResult = await this.batchExecutor.executePrepareHooks({
888
+ steps: batchSteps,
889
+ context,
890
+ data: session.data,
891
+ executeHook,
892
+ });
893
+
894
+ if (!prepareResult.success) {
895
+ // Prepare hook failed - return error response
896
+ logger.error(`[ResponseModal] Prepare hook failed:`, prepareResult.error);
897
+ throw new ResponseGenerationError(
898
+ `Prepare hook failed: ${prepareResult.error?.message}`,
899
+ {
900
+ phase: 'prepare_hooks',
901
+ context: {
902
+ stepId: prepareResult.error?.stepId,
903
+ executedSteps: prepareResult.executedSteps,
904
+ }
905
+ }
906
+ );
907
+ }
908
+
909
+ // PHASE 2: Build combined prompt using BatchPromptBuilder (Requirement 4.4)
910
+ logger.debug(`[ResponseModal] Building batch prompt`);
911
+ const batchPromptResult = await this.batchPromptBuilder.buildBatchPrompt({
912
+ steps: batchSteps,
913
+ route: selectedRoute,
914
+ history: historyEvents,
915
+ context,
916
+ session,
917
+ agentOptions: this.agent.getAgentOptions(),
918
+ });
919
+
920
+ logger.debug(`[ResponseModal] Batch prompt built with ${batchPromptResult.stepCount} steps, collecting: ${batchPromptResult.collectFields.join(', ')}`);
921
+
922
+ // Build response schema for batch (includes all collect fields)
923
+ const responseSchema = this.buildBatchResponseSchema(batchPromptResult.collectFields);
924
+
925
+ // Collect available tools for AI (from all steps in batch)
926
+ const availableTools = this.collectBatchAvailableTools(selectedRoute, batchSteps);
927
+
928
+ // PHASE 3: Make single LLM call (Requirement 4.4)
929
+ logger.debug(`[ResponseModal] Making LLM call for batch`);
930
+ const agentOptions = this.agent.getAgentOptions();
931
+ const result = await agentOptions.provider.generateMessage({
932
+ prompt: batchPromptResult.prompt,
933
+ history: historyEvents,
934
+ context,
935
+ tools: availableTools,
936
+ signal,
937
+ parameters: responseSchema ? { jsonSchema: responseSchema, schemaName: "batch_response" } : undefined,
938
+ });
939
+
940
+ let message = result.structured?.message || result.message;
941
+ let toolCalls = result.structured?.toolCalls;
942
+
943
+ logger.debug(`[ResponseModal] LLM response received for batch`);
944
+
945
+ // Execute tools if any
946
+ if (toolCalls && toolCalls.length > 0) {
947
+ const toolResult = await this.executeUnifiedToolLoop({
948
+ toolCalls,
949
+ context,
950
+ session,
951
+ history,
952
+ selectedRoute,
953
+ responsePrompt: batchPromptResult.prompt,
954
+ availableTools,
955
+ responseSchema,
956
+ signal,
957
+ });
958
+
959
+ session = toolResult.session;
960
+ toolCalls = toolResult.finalToolCalls;
961
+ if (toolResult.finalMessage) {
962
+ message = toolResult.finalMessage;
963
+ }
964
+ }
965
+
966
+ // PHASE 4: Collect data from response for all steps (Requirement 6.1, 6.2, 6.3)
967
+ logger.debug(`[ResponseModal] Collecting batch data`);
968
+ const collectResult = this.batchExecutor.collectBatchData({
969
+ steps: batchSteps,
970
+ llmResponse: result.structured || {},
971
+ session,
972
+ schema: this.agent.getSchema(),
973
+ });
974
+
975
+ session = collectResult.session;
976
+
977
+ if (collectResult.collectedData && Object.keys(collectResult.collectedData).length > 0) {
978
+ // Update agent's collected data
979
+ await this.agent.updateCollectedData(collectResult.collectedData);
980
+ logger.debug(`[ResponseModal] Batch collected data:`, collectResult.collectedData);
981
+ }
982
+
983
+ if (collectResult.validationErrors && collectResult.validationErrors.length > 0) {
984
+ logger.warn(`[ResponseModal] Batch data validation errors:`, collectResult.validationErrors);
985
+ }
986
+
987
+ // Update session to final step position
988
+ const lastStep = batchSteps[batchSteps.length - 1];
989
+ if (lastStep?.id) {
990
+ session = enterStep(session, lastStep.id, lastStep.description);
991
+ logger.debug(`[ResponseModal] Updated session to final batch step: ${lastStep.id}`);
992
+ }
993
+
994
+ // PHASE 5: Execute all finalize hooks (Requirement 5.2)
995
+ logger.debug(`[ResponseModal] Executing finalize hooks for batch`);
996
+ const finalizeResult = await this.batchExecutor.executeFinalizeHooks({
997
+ steps: batchSteps,
998
+ context,
999
+ data: session.data,
1000
+ executeHook,
1001
+ });
1002
+
1003
+ if (finalizeResult.errors && finalizeResult.errors.length > 0) {
1004
+ // Log finalize errors but don't fail (Requirement 5.5)
1005
+ logger.warn(`[ResponseModal] Some finalize hooks failed:`, finalizeResult.errors);
1006
+ }
1007
+
1008
+ // Build executed steps list
1009
+ const executedSteps: StepRef[] = batchSteps
1010
+ .filter(step => step.id)
1011
+ .map(step => ({
1012
+ id: step.id!,
1013
+ routeId: selectedRoute.id,
1014
+ }));
1015
+
1016
+ logger.debug(`[ResponseModal] Batch execution complete. Executed ${executedSteps.length} steps`);
1017
+
1018
+ return {
1019
+ message,
1020
+ toolCalls,
1021
+ session,
1022
+ executedSteps,
1023
+ };
1024
+ }
1025
+
1026
+ /**
1027
+ * Build response schema for batch execution
1028
+ * @private
1029
+ */
1030
+ private buildBatchResponseSchema(collectFields: string[]): Record<string, unknown> {
1031
+ const properties: Record<string, unknown> = {
1032
+ message: {
1033
+ type: "string",
1034
+ description: "Your response to the user",
1035
+ },
1036
+ };
1037
+
1038
+ // Add collect fields to schema
1039
+ for (const field of collectFields) {
1040
+ properties[field] = {
1041
+ type: "string",
1042
+ description: `Collected value for ${field}`,
1043
+ };
1044
+ }
1045
+
1046
+ return {
1047
+ type: "object",
1048
+ properties,
1049
+ required: ["message"],
1050
+ additionalProperties: true,
553
1051
  };
554
1052
  }
555
1053
 
1054
+ /**
1055
+ * Collect available tools from all steps in the batch
1056
+ * @private
1057
+ */
1058
+ private collectBatchAvailableTools(
1059
+ route: Route<TContext, TData>,
1060
+ batchSteps: StepOptions<TContext, TData>[]
1061
+ ): Array<{
1062
+ id: string;
1063
+ name: string;
1064
+ description?: string;
1065
+ parameters?: unknown;
1066
+ }> {
1067
+ const availableTools = new Map<string, Tool<TContext, TData>>();
1068
+
1069
+ // Add agent-level tools
1070
+ this.agent.getTools().forEach((tool) => {
1071
+ availableTools.set(tool.id, tool);
1072
+ });
1073
+
1074
+ // Add route-level tools
1075
+ route.getTools().forEach((tool: Tool<TContext, TData>) => {
1076
+ availableTools.set(tool.id, tool);
1077
+ });
1078
+
1079
+ // Add step-level tools from all batch steps
1080
+ for (const step of batchSteps) {
1081
+ if (step.tools) {
1082
+ for (const toolRef of step.tools) {
1083
+ if (typeof toolRef === "string") {
1084
+ // Reference to registered tool - already in availableTools
1085
+ } else if (typeof toolRef === 'object' && 'id' in toolRef && toolRef.id) {
1086
+ // Inline tool definition
1087
+ availableTools.set(toolRef.id, toolRef);
1088
+ }
1089
+ }
1090
+ }
1091
+ }
1092
+
1093
+ // Convert to the format expected by AI providers
1094
+ return Array.from(availableTools.values()).map((tool) => ({
1095
+ id: tool.id,
1096
+ name: tool.name || tool.id,
1097
+ description: tool.description,
1098
+ parameters: tool.parameters,
1099
+ }));
1100
+ }
1101
+
556
1102
  /**
557
1103
  * Unified streaming response generation
558
1104
  * @private
@@ -560,7 +1106,17 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
560
1106
  private async *generateUnifiedStreamingResponse(
561
1107
  responseContext: ResponseContext<TContext, TData>
562
1108
  ): AsyncGenerator<AgentResponseStreamChunk<TData>> {
563
- const { effectiveContext, session: initialSession, history, selectedRoute, selectedStep, responseDirectives, isRouteComplete } = responseContext;
1109
+ const {
1110
+ effectiveContext,
1111
+ session: initialSession,
1112
+ history,
1113
+ selectedRoute,
1114
+ selectedStep,
1115
+ responseDirectives,
1116
+ isRouteComplete,
1117
+ batchSteps,
1118
+ batchStoppedReason,
1119
+ } = responseContext;
564
1120
  const session = initialSession;
565
1121
 
566
1122
  // Get last user message (needed for both route and completion handling)
@@ -569,17 +1125,35 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
569
1125
  const lastMessageText = getLastMessageFromHistory(historyEvents);
570
1126
 
571
1127
  if (selectedRoute && !isRouteComplete) {
572
- // Handle normal route processing with streaming
573
- yield* this.processRouteStreamingResponse({
574
- selectedRoute,
575
- selectedStep,
576
- responseDirectives,
577
- session,
578
- history,
579
- context: effectiveContext,
580
- lastMessageText,
581
- historyEvents,
582
- });
1128
+ // Check if we have batch steps to execute
1129
+ if (batchSteps && batchSteps.length > 0) {
1130
+ // BATCH EXECUTION: Execute multiple steps with streaming
1131
+ // Note: For streaming, we still use batch execution but stream the response
1132
+ logger.debug(`[ResponseModal] Streaming batch execution for ${batchSteps.length} steps`);
1133
+
1134
+ yield* this.streamBatchResponse({
1135
+ selectedRoute,
1136
+ batchSteps,
1137
+ responseDirectives,
1138
+ session,
1139
+ history,
1140
+ context: effectiveContext,
1141
+ historyEvents,
1142
+ batchStoppedReason,
1143
+ });
1144
+ } else {
1145
+ // SINGLE STEP EXECUTION: Fall back to single-step streaming
1146
+ yield* this.processRouteStreamingResponse({
1147
+ selectedRoute,
1148
+ selectedStep,
1149
+ responseDirectives,
1150
+ session,
1151
+ history,
1152
+ context: effectiveContext,
1153
+ lastMessageText,
1154
+ historyEvents,
1155
+ });
1156
+ }
583
1157
 
584
1158
  } else if (isRouteComplete && selectedRoute) {
585
1159
  // Handle route completion streaming
@@ -600,6 +1174,148 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
600
1174
  });
601
1175
  }
602
1176
  }
1177
+
1178
+ /**
1179
+ * Stream a batch response with multiple steps
1180
+ *
1181
+ * Similar to executeBatchResponse but streams the LLM response.
1182
+ *
1183
+ * @private
1184
+ */
1185
+ private async *streamBatchResponse(params: {
1186
+ selectedRoute: Route<TContext, TData>;
1187
+ batchSteps: StepOptions<TContext, TData>[];
1188
+ responseDirectives?: string[];
1189
+ session: SessionState<TData>;
1190
+ history: HistoryItem[];
1191
+ context: TContext;
1192
+ historyEvents: Event[];
1193
+ batchStoppedReason?: StoppedReason;
1194
+ signal?: AbortSignal;
1195
+ }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
1196
+ const { selectedRoute, batchSteps, context, historyEvents, batchStoppedReason, signal } = params;
1197
+ let session = params.session;
1198
+
1199
+ // Create hook executor function
1200
+ const executeHook = async (
1201
+ hook: HookFunction<TContext, TData>,
1202
+ hookContext: TContext,
1203
+ data?: Partial<TData>,
1204
+ step?: StepOptions<TContext, TData>
1205
+ ): Promise<void> => {
1206
+ const route = selectedRoute;
1207
+ const stepInstance = step?.id ? route.getStep(step.id) : undefined;
1208
+ await this.executePrepareFinalize(hook, hookContext, data, route, stepInstance);
1209
+ };
1210
+
1211
+ // PHASE 1: Execute all prepare hooks
1212
+ const prepareResult = await this.batchExecutor.executePrepareHooks({
1213
+ steps: batchSteps,
1214
+ context,
1215
+ data: session.data,
1216
+ executeHook,
1217
+ });
1218
+
1219
+ if (!prepareResult.success) {
1220
+ // Yield error chunk
1221
+ yield {
1222
+ delta: "",
1223
+ accumulated: "",
1224
+ done: true,
1225
+ session,
1226
+ error: new ResponseGenerationError(
1227
+ `Prepare hook failed: ${prepareResult.error?.message}`,
1228
+ { phase: 'prepare_hooks' }
1229
+ ),
1230
+ };
1231
+ return;
1232
+ }
1233
+
1234
+ // PHASE 2: Build combined prompt
1235
+ const batchPromptResult = await this.batchPromptBuilder.buildBatchPrompt({
1236
+ steps: batchSteps,
1237
+ route: selectedRoute,
1238
+ history: historyEvents,
1239
+ context,
1240
+ session,
1241
+ agentOptions: this.agent.getAgentOptions(),
1242
+ });
1243
+
1244
+ const responseSchema = this.buildBatchResponseSchema(batchPromptResult.collectFields);
1245
+ const availableTools = this.collectBatchAvailableTools(selectedRoute, batchSteps);
1246
+
1247
+ // PHASE 3: Stream LLM response
1248
+ const agentOptions = this.agent.getAgentOptions();
1249
+ const stream = agentOptions.provider.generateMessageStream({
1250
+ prompt: batchPromptResult.prompt,
1251
+ history: historyEvents,
1252
+ context,
1253
+ tools: availableTools,
1254
+ signal,
1255
+ parameters: responseSchema ? { jsonSchema: responseSchema, schemaName: "batch_stream_response" } : undefined,
1256
+ });
1257
+
1258
+ // Build executed steps list
1259
+ const executedSteps: StepRef[] = batchSteps
1260
+ .filter(step => step.id)
1261
+ .map(step => ({
1262
+ id: step.id!,
1263
+ routeId: selectedRoute.id,
1264
+ }));
1265
+
1266
+ // Stream chunks
1267
+ for await (const chunk of stream) {
1268
+ // On final chunk, collect data and execute finalize hooks
1269
+ if (chunk.done) {
1270
+ // Collect data from response
1271
+ if (chunk.structured) {
1272
+ const collectResult = this.batchExecutor.collectBatchData({
1273
+ steps: batchSteps,
1274
+ llmResponse: chunk.structured,
1275
+ session,
1276
+ schema: this.agent.getSchema(),
1277
+ });
1278
+
1279
+ session = collectResult.session;
1280
+
1281
+ if (collectResult.collectedData && Object.keys(collectResult.collectedData).length > 0) {
1282
+ await this.agent.updateCollectedData(collectResult.collectedData);
1283
+ }
1284
+ }
1285
+
1286
+ // Update session to final step position
1287
+ const lastStep = batchSteps[batchSteps.length - 1];
1288
+ if (lastStep?.id) {
1289
+ session = enterStep(session, lastStep.id, lastStep.description);
1290
+ }
1291
+
1292
+ // Execute finalize hooks
1293
+ await this.batchExecutor.executeFinalizeHooks({
1294
+ steps: batchSteps,
1295
+ context,
1296
+ data: session.data,
1297
+ executeHook,
1298
+ });
1299
+
1300
+ // Finalize session
1301
+ await this.finalizeSession(session, context);
1302
+ }
1303
+
1304
+ yield {
1305
+ delta: chunk.delta,
1306
+ accumulated: chunk.accumulated,
1307
+ done: chunk.done,
1308
+ session,
1309
+ toolCalls: chunk.structured?.toolCalls,
1310
+ isRouteComplete: false,
1311
+ executedSteps: chunk.done ? executedSteps : undefined,
1312
+ stoppedReason: chunk.done ? batchStoppedReason : undefined,
1313
+ metadata: chunk.metadata,
1314
+ structured: chunk.structured,
1315
+ };
1316
+ }
1317
+ }
1318
+
603
1319
  /**
604
1320
  * Execute prepare function for current step if available
605
1321
  * @private
@@ -679,12 +1395,27 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
679
1395
  if (selectedStep) {
680
1396
  nextStep = selectedStep;
681
1397
  } else {
682
- // New route or no step selected - get initial step or first valid step
1398
+ // Determine current step from session if we're already in this route
1399
+ const isInSameRoute = session.currentRoute?.id === selectedRoute.id;
1400
+ const currentStep = isInSameRoute && session.currentStep
1401
+ ? selectedRoute.getStep(session.currentStep.id)
1402
+ : undefined;
1403
+
1404
+ logger.debug(`[ResponseModal] Step determination: route match=${isInSameRoute}, currentRoute=${session.currentRoute?.id}, selectedRoute=${selectedRoute.id}, currentStep=${currentStep?.id || 'none'}`);
1405
+
1406
+ // Get candidate steps based on current position in the route
683
1407
  const routingEngine = this.agent.getRoutingEngine();
684
- const candidates = routingEngine.getCandidateSteps(selectedRoute, undefined, session.data || {});
1408
+ const candidates = await routingEngine.getCandidateStepsWithConditions(
1409
+ selectedRoute,
1410
+ currentStep, // Pass current step instead of undefined to maintain progression
1411
+ createTemplateContext({ data: session.data, session, context })
1412
+ );
1413
+
1414
+ logger.debug(`[ResponseModal] Found ${candidates.length} candidate steps${currentStep ? ' from current step ' + currentStep.id : ' (new route entry)'}`);
1415
+
685
1416
  if (candidates.length > 0) {
686
1417
  nextStep = candidates[0].step;
687
- logger.debug(`[ResponseModal] Using first valid step: ${nextStep.id} for new route`);
1418
+ logger.debug(`[ResponseModal] Using first valid step: ${nextStep.id}${currentStep ? ' (progressing from ' + currentStep.id + ')' : ' for new route'}`);
688
1419
  } else {
689
1420
  // Fallback to initial step even if it should be skipped
690
1421
  nextStep = selectedRoute.initialStep;
@@ -733,6 +1464,15 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
733
1464
  let message = result.structured?.message || result.message;
734
1465
  let toolCalls = result.structured?.toolCalls;
735
1466
 
1467
+ // Debug: Log initial AI response
1468
+ logger.debug(`[ResponseModal] Initial AI response:`, {
1469
+ hasMessage: !!message,
1470
+ messageLength: message?.length || 0,
1471
+ hasToolCalls: !!toolCalls,
1472
+ toolCallsCount: toolCalls?.length || 0,
1473
+ toolNames: toolCalls?.map(tc => tc.toolName) || [],
1474
+ });
1475
+
736
1476
  // Execute tools with unified loop handling
737
1477
  const toolResult = await this.executeUnifiedToolLoop({
738
1478
  toolCalls,
@@ -781,11 +1521,22 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
781
1521
  if (selectedStep) {
782
1522
  nextStep = selectedStep;
783
1523
  } else {
1524
+ // Determine current step from session if we're already in this route
1525
+ const currentStep = session.currentRoute?.id === selectedRoute.id && session.currentStep
1526
+ ? selectedRoute.getStep(session.currentStep.id)
1527
+ : undefined;
1528
+
1529
+ // Get candidate steps based on current position in the route
784
1530
  const routingEngine = this.agent.getRoutingEngine();
785
- const candidates = routingEngine.getCandidateSteps(selectedRoute, undefined, session.data || {});
1531
+ const candidates = await routingEngine.getCandidateStepsWithConditions(
1532
+ selectedRoute,
1533
+ currentStep, // Pass current step instead of undefined to maintain progression
1534
+ createTemplateContext({ data: session.data, session, context })
1535
+ );
1536
+
786
1537
  if (candidates.length > 0) {
787
1538
  nextStep = candidates[0].step;
788
- logger.debug(`[ResponseModal] Using first valid step: ${nextStep.id} for new route`);
1539
+ logger.debug(`[ResponseModal] Using first valid step: ${nextStep.id}${currentStep ? ' (progressing from ' + currentStep.id + ')' : ' for new route'}`);
789
1540
  } else {
790
1541
  nextStep = selectedRoute.initialStep;
791
1542
  logger.warn(`[ResponseModal] No valid steps found, using initial step: ${nextStep.id}`);
@@ -868,6 +1619,10 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
868
1619
  await this.finalizeSession(session, context);
869
1620
  }
870
1621
 
1622
+ // Response structure completeness (Requirement 8.1, 8.2, 8.3)
1623
+ // - executedSteps: single step executed in this response
1624
+ // - stoppedReason: 'needs_input' for single-step execution (waiting for user input)
1625
+ // - session.currentStep: reflects the executed step
871
1626
  yield {
872
1627
  delta: chunk.delta,
873
1628
  accumulated: chunk.accumulated,
@@ -875,6 +1630,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
875
1630
  session,
876
1631
  toolCalls,
877
1632
  isRouteComplete: false,
1633
+ executedSteps: chunk.done ? [{ id: nextStep.id, routeId: selectedRoute.id }] : undefined,
1634
+ stoppedReason: chunk.done ? 'needs_input' : undefined,
878
1635
  metadata: chunk.metadata,
879
1636
  structured: chunk.structured,
880
1637
  };
@@ -915,7 +1672,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
915
1672
 
916
1673
  // Execute initial dynamic tool calls
917
1674
  if (toolCalls && toolCalls.length > 0) {
918
- logger.debug(`[ResponseModal] Executing ${toolCalls.length} dynamic tool calls`);
1675
+ logger.debug(`[ResponseModal] Executing ${toolCalls.length} dynamic tool calls:`, toolCalls.map(tc => tc.toolName));
919
1676
 
920
1677
  for (const toolCall of toolCalls) {
921
1678
  const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
@@ -925,16 +1682,24 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
925
1682
  }
926
1683
 
927
1684
  try {
928
- const toolExecutor = new ToolExecutor<TContext, TData>();
929
- const toolResult = await toolExecutor.executeTool({
930
- tool: tool,
931
- context,
932
- updateContext: this.agent.updateContext.bind(this.agent),
933
- updateData: this.agent.updateCollectedData.bind(this.agent),
934
- history: historyEvents, // Use Event[] for tool execution
935
- data: session.data,
936
- toolArguments: toolCall.arguments,
937
- });
1685
+ // Use ToolManager for unified tool execution
1686
+ const toolManager = this.getToolManager();
1687
+ let toolResult;
1688
+
1689
+ if (toolManager) {
1690
+ toolResult = await toolManager.executeTool({
1691
+ tool: tool,
1692
+ context,
1693
+ updateContext: this.agent.updateContext.bind(this.agent),
1694
+ updateData: this.agent.updateCollectedData.bind(this.agent),
1695
+ history: historyEvents, // Use Event[] for tool execution
1696
+ data: session.data,
1697
+ toolArguments: toolCall.arguments,
1698
+ });
1699
+ } else {
1700
+ // Fallback: execute tool directly if ToolManager not available
1701
+ throw new Error(`ToolManager not available for tool execution: ${toolCall.toolName}`);
1702
+ }
938
1703
 
939
1704
  // Check if tool execution was successful
940
1705
  if (!toolResult.success) {
@@ -965,7 +1730,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
965
1730
  }
966
1731
  }
967
1732
 
968
- logger.debug(`[ResponseModal] Executed dynamic tool: ${toolResult.toolName} (success: ${toolResult.success})`);
1733
+ logger.debug(`[ResponseModal] Executed dynamic tool: ${toolCall.toolName} (success: ${toolResult.success})`);
969
1734
  } catch (error) {
970
1735
  logger.error(`[ResponseModal] Tool execution error for ${toolCall.toolName}:`, error);
971
1736
  // Continue with other tools rather than failing the entire response
@@ -982,7 +1747,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
982
1747
 
983
1748
  while (hasToolCalls && toolLoopCount < MAX_TOOL_LOOPS) {
984
1749
  toolLoopCount++;
985
- logger.debug(`[ResponseModal] Starting tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS}`);
1750
+ logger.debug(`[ResponseModal] Starting tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS} with ${toolCalls?.length || 0} tool calls`);
986
1751
 
987
1752
  // Create tool result events with proper Event format structure
988
1753
  const toolResultEvents: Event<ToolEventData>[] = [];
@@ -1014,12 +1779,21 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1014
1779
  const updatedHistoryEvents = [...historyEvents, ...toolResultEvents];
1015
1780
 
1016
1781
  // Make follow-up AI call to see if more tools are needed
1782
+ // After first iteration, don't provide tools to force a text response
1017
1783
  const agentOptions = this.agent.getAgentOptions();
1784
+ const shouldProvideTools = toolLoopCount === 1;
1785
+
1786
+ logger.debug(`[ResponseModal] Making follow-up AI call (loop ${toolLoopCount}):`, {
1787
+ providingTools: shouldProvideTools,
1788
+ toolsCount: shouldProvideTools ? availableTools.length : 0,
1789
+ addingTextInstruction: toolLoopCount > 1,
1790
+ });
1791
+
1018
1792
  const followUpResult = await agentOptions.provider.generateMessage({
1019
- prompt: responsePrompt,
1793
+ prompt: responsePrompt + (toolLoopCount > 1 ? "\n\nProvide a text response to the user based on the tool results." : ""),
1020
1794
  history: updatedHistoryEvents, // Use Event[] for AI provider
1021
1795
  context,
1022
- tools: availableTools,
1796
+ tools: shouldProvideTools ? availableTools : [], // Only provide tools on first iteration
1023
1797
  parameters: responseSchema ? {
1024
1798
  jsonSchema: responseSchema,
1025
1799
  schemaName: "tool_followup",
@@ -1031,6 +1805,14 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1031
1805
  const followUpToolCalls = followUpResult.structured?.toolCalls;
1032
1806
  hasToolCalls = followUpToolCalls && followUpToolCalls.length > 0;
1033
1807
 
1808
+ logger.debug(`[ResponseModal] Follow-up AI response (loop ${toolLoopCount}):`, {
1809
+ hasMessage: !!followUpResult.message,
1810
+ messageLength: followUpResult.message?.length || 0,
1811
+ hasToolCalls,
1812
+ toolCallsCount: followUpToolCalls?.length || 0,
1813
+ toolNames: followUpToolCalls?.map(tc => tc.toolName) || [],
1814
+ });
1815
+
1034
1816
  if (hasToolCalls) {
1035
1817
  logger.debug(`[ResponseModal] Follow-up call produced ${followUpToolCalls!.length} additional tool calls`);
1036
1818
 
@@ -1043,16 +1825,24 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1043
1825
  }
1044
1826
 
1045
1827
  try {
1046
- const toolExecutor = new ToolExecutor<TContext, TData>();
1047
- const toolResult = await toolExecutor.executeTool({
1048
- tool: tool,
1049
- context,
1050
- updateContext: this.agent.updateContext.bind(this.agent),
1051
- updateData: this.agent.updateCollectedData.bind(this.agent),
1052
- history: updatedHistoryEvents, // Use Event[] for tool execution
1053
- data: session.data,
1054
- toolArguments: toolCall.arguments,
1055
- });
1828
+ // Use ToolManager for unified tool execution
1829
+ const toolManager = this.getToolManager();
1830
+ let toolResult;
1831
+
1832
+ if (toolManager) {
1833
+ toolResult = await toolManager.executeTool({
1834
+ tool: tool,
1835
+ context,
1836
+ updateContext: this.agent.updateContext.bind(this.agent),
1837
+ updateData: this.agent.updateCollectedData.bind(this.agent),
1838
+ history: updatedHistoryEvents, // Use Event[] for tool execution
1839
+ data: session.data,
1840
+ toolArguments: toolCall.arguments,
1841
+ });
1842
+ } else {
1843
+ // Fallback: execute tool directly if ToolManager not available
1844
+ throw new Error(`ToolManager not available for follow-up tool execution: ${toolCall.toolName}`);
1845
+ }
1056
1846
 
1057
1847
  // Check if tool execution was successful
1058
1848
  if (!toolResult.success) {
@@ -1079,7 +1869,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1079
1869
  }
1080
1870
  }
1081
1871
 
1082
- logger.debug(`[ResponseModal] Executed follow-up tool: ${toolResult.toolName} (success: ${toolResult.success})`);
1872
+ logger.debug(`[ResponseModal] Executed follow-up tool: ${toolCall.toolName} (success: ${toolResult.success})`);
1083
1873
  } catch (error) {
1084
1874
  logger.error(`[ResponseModal] Follow-up tool execution error for ${toolCall.toolName}:`, error);
1085
1875
  continue;
@@ -1101,6 +1891,13 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1101
1891
  logger.warn(`[ResponseModal] Tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`);
1102
1892
  }
1103
1893
 
1894
+ logger.debug(`[ResponseModal] Tool loop completed:`, {
1895
+ totalIterations: toolLoopCount,
1896
+ hasFinalMessage: !!finalMessage,
1897
+ finalMessageLength: finalMessage?.length || 0,
1898
+ finalToolCallsCount: toolCalls?.length || 0,
1899
+ });
1900
+
1104
1901
  return {
1105
1902
  session,
1106
1903
  finalToolCalls: toolCalls,
@@ -1127,15 +1924,34 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1127
1924
  let updatedSession = session;
1128
1925
 
1129
1926
  // Extract collected data from final response (only for route-based interactions)
1130
- if (selectedRoute && result.structured && nextStep?.collect) {
1927
+ if (selectedRoute && result.structured) {
1131
1928
  try {
1132
1929
  const collectedData: Record<string, unknown> = {};
1133
1930
  // AgentStructuredResponse extends Record<string, unknown>, so we can safely access properties
1134
1931
  const structuredData = result.structured;
1135
1932
 
1136
- for (const field of nextStep.collect) {
1933
+ // Collect ALL route fields (required + optional) from structured response
1934
+ const allRouteFields = new Set<string>();
1935
+
1936
+ // Add route required fields
1937
+ if (selectedRoute.requiredFields) {
1938
+ selectedRoute.requiredFields.forEach(field => allRouteFields.add(String(field)));
1939
+ }
1940
+
1941
+ // Add route optional fields
1942
+ if (selectedRoute.optionalFields) {
1943
+ selectedRoute.optionalFields.forEach(field => allRouteFields.add(String(field)));
1944
+ }
1945
+
1946
+ // Also include current step's collect fields (in case they're not in route fields)
1947
+ if (nextStep?.collect) {
1948
+ nextStep.collect.forEach(field => allRouteFields.add(String(field)));
1949
+ }
1950
+
1951
+ // Extract all available fields from structured response
1952
+ for (const field of allRouteFields) {
1137
1953
  const fieldKey = String(field);
1138
- if (fieldKey in structuredData) {
1954
+ if (fieldKey in structuredData && structuredData[fieldKey] !== undefined && structuredData[fieldKey] !== null) {
1139
1955
  collectedData[fieldKey] = structuredData[fieldKey];
1140
1956
  }
1141
1957
  }
@@ -1209,37 +2025,66 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1209
2025
  prompt: endStepSpec.prompt || "Summarize what was accomplished and confirm completion based on the conversation history and collected data",
1210
2026
  });
1211
2027
 
1212
- // Build response schema for completion
1213
- const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, completionStep, this.agent.getSchema());
1214
- const templateContext = { context, session, history: historyEvents }; // Use Event[] for template context
2028
+ // Build response schema for completion (message only, no data collection)
2029
+ const completionSchema = {
2030
+ type: "object",
2031
+ properties: {
2032
+ message: {
2033
+ type: "string",
2034
+ description: "Completion message confirming what was accomplished",
2035
+ },
2036
+ },
2037
+ required: ["message"],
2038
+ additionalProperties: false,
2039
+ };
2040
+
2041
+ const templateContext = createTemplateContext({ context, session, history: historyEvents });
2042
+
2043
+ // Build completion response prompt using ResponseEngine
2044
+ // Filter out conditional guidelines - only include always-active ones
2045
+ const alwaysActiveGuidelines = [
2046
+ ...this.agent.getGuidelines().filter(g => !g.condition),
2047
+ ...selectedRoute.getGuidelines().filter(g => !g.condition),
2048
+ ];
2049
+ let completitionPrompt = "Summarize what was accomplished and confirm completion"
2050
+ if(endStepSpec.prompt){
2051
+ completitionPrompt = await render(endStepSpec.prompt, templateContext)
2052
+ }
1215
2053
 
1216
- // Build completion response prompt
1217
2054
  const completionPrompt = await this.responseEngine.buildResponsePrompt({
1218
2055
  route: selectedRoute,
1219
2056
  currentStep: completionStep,
1220
2057
  rules: selectedRoute.getRules(),
1221
2058
  prohibitions: selectedRoute.getProhibitions(),
1222
- directives: undefined, // No directives for completion
1223
- history: historyEvents, // Use Event[] for buildResponsePrompt
1224
- lastMessage: lastMessageText, // Use string for buildResponsePrompt
2059
+ directives: [
2060
+ `Task completed: ${selectedRoute.title}`,
2061
+ `Collected data: ${JSON.stringify(session.data, null, 2)}`,
2062
+ "Do NOT ask for more information - the task is complete",
2063
+ completitionPrompt,
2064
+ ],
2065
+ history: historyEvents,
2066
+ lastMessage: lastMessageText,
1225
2067
  agentOptions: this.agent.getAgentOptions(),
1226
- combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
2068
+ combinedGuidelines: alwaysActiveGuidelines, // Only non-conditional guidelines
1227
2069
  combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
1228
2070
  context,
1229
2071
  session,
1230
- agentSchema: this.agent.getSchema(),
2072
+ agentSchema: undefined, // No data collection schema for completion
1231
2073
  });
1232
2074
 
1233
2075
  // Generate completion message using AI provider
1234
2076
  const agentOptions = this.agent.getAgentOptions();
2077
+ logger.debug(`[ResponseModal] Calling AI provider for completion message...`);
2078
+
1235
2079
  const completionResult = await agentOptions.provider.generateMessage({
1236
2080
  prompt: completionPrompt,
1237
- history: historyEvents, // Use Event[] for AI provider
2081
+ history: historyEvents,
1238
2082
  context,
1239
2083
  signal,
1240
- parameters: { jsonSchema: responseSchema, schemaName: "completion_message" },
2084
+ parameters: { jsonSchema: completionSchema, schemaName: "completion_message" },
1241
2085
  });
1242
2086
 
2087
+ logger.debug(`[ResponseModal] AI provider returned completion result`);
1243
2088
  const message = completionResult.structured?.message || completionResult.message;
1244
2089
  logger.debug(`[ResponseModal] Generated completion message for route: ${selectedRoute.title}`);
1245
2090
 
@@ -1298,7 +2143,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1298
2143
 
1299
2144
  // Build response schema for completion
1300
2145
  const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, completionStep, this.agent.getSchema());
1301
- const templateContext = { context, session, history: historyEvents }; // Use Event[] for template context
2146
+ const templateContext = createTemplateContext({ context, session, history: historyEvents }); // Use Event[] for template context
1302
2147
 
1303
2148
  // Build completion response prompt
1304
2149
  const completionPrompt = await this.responseEngine.buildResponsePrompt({
@@ -1366,6 +2211,10 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1366
2211
  await this.finalizeSession(session, context);
1367
2212
  }
1368
2213
 
2214
+ // Response structure completeness (Requirement 8.1, 8.2, 8.3)
2215
+ // - executedSteps: empty for route completion (no new steps executed)
2216
+ // - stoppedReason: 'route_complete' for completed routes
2217
+ // - session.currentStep: set to END_ROUTE
1369
2218
  yield {
1370
2219
  delta: chunk.delta,
1371
2220
  accumulated: chunk.accumulated,
@@ -1373,6 +2222,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1373
2222
  session,
1374
2223
  toolCalls: undefined,
1375
2224
  isRouteComplete: true,
2225
+ executedSteps: chunk.done ? [] : undefined,
2226
+ stoppedReason: chunk.done ? 'route_complete' : undefined,
1376
2227
  metadata: chunk.metadata,
1377
2228
  structured: chunk.structured,
1378
2229
  };
@@ -1467,6 +2318,10 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1467
2318
  await this.finalizeSession(session, context);
1468
2319
  }
1469
2320
 
2321
+ // Response structure completeness (Requirement 8.1, 8.2, 8.3)
2322
+ // - executedSteps: empty for fallback (no route/step execution)
2323
+ // - stoppedReason: undefined for fallback (no route context)
2324
+ // - session.currentStep: unchanged (no step progression)
1470
2325
  yield {
1471
2326
  delta: chunk.delta,
1472
2327
  accumulated: chunk.accumulated,
@@ -1474,6 +2329,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1474
2329
  session,
1475
2330
  toolCalls: undefined,
1476
2331
  isRouteComplete: false,
2332
+ executedSteps: chunk.done ? [] : undefined,
2333
+ stoppedReason: undefined,
1477
2334
  metadata: chunk.metadata,
1478
2335
  structured: chunk.structured,
1479
2336
  };
@@ -1511,19 +2368,28 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1511
2368
  // ============================================================================
1512
2369
 
1513
2370
  /**
1514
- * Find an available tool by name for the given route
1515
- * Route-level tools take precedence over agent-level tools
2371
+ * Find an available tool by name for the given route using ToolManager
2372
+ * Delegates to ToolManager for unified tool resolution
1516
2373
  * @private
1517
2374
  */
1518
2375
  private findAvailableTool(
1519
2376
  toolName: string,
1520
2377
  route?: Route<TContext, TData>
1521
- ): Tool<TContext, TData, unknown[], unknown> | undefined {
2378
+ ): Tool<TContext, TData> | undefined {
2379
+ // Use ToolManager for unified tool resolution
2380
+ const toolManager = this.getToolManager();
2381
+ if (toolManager) {
2382
+ return toolManager.find(toolName, undefined, undefined, route);
2383
+ }
2384
+
2385
+ // Fallback to legacy resolution if ToolManager not available
2386
+ logger.warn(`[ResponseModal] ToolManager not available, using legacy tool resolution for: ${toolName}`);
2387
+
1522
2388
  // Check route-level tools first (if route provided)
1523
2389
  if (route) {
1524
2390
  const routeTool = route
1525
2391
  .getTools()
1526
- .find((tool: Tool<TContext, TData, unknown[], unknown>) => tool.id === toolName || tool.name === toolName);
2392
+ .find((tool: Tool<TContext, TData>) => tool.id === toolName || tool.name === toolName);
1527
2393
  if (routeTool) return routeTool;
1528
2394
  }
1529
2395
 
@@ -1535,7 +2401,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1535
2401
  }
1536
2402
 
1537
2403
  /**
1538
- * Collect all available tools for the given route and step context
2404
+ * Collect all available tools for the given route and step context using ToolManager
2405
+ * Delegates to ToolManager for unified tool resolution and deduplication
1539
2406
  * @private
1540
2407
  */
1541
2408
  private collectAvailableTools(
@@ -1547,7 +2414,22 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1547
2414
  description?: string;
1548
2415
  parameters?: unknown;
1549
2416
  }> {
1550
- const availableTools = new Map<string, Tool<TContext, TData, unknown[], unknown>>();
2417
+ // Use ToolManager for unified tool collection if available
2418
+ const toolManager = this.getToolManager();
2419
+ if (toolManager) {
2420
+ const availableTools = toolManager.getAvailable(undefined, step, route);
2421
+ return availableTools.map((tool) => ({
2422
+ id: tool.id,
2423
+ name: tool.name || tool.id,
2424
+ description: tool.description,
2425
+ parameters: tool.parameters,
2426
+ }));
2427
+ }
2428
+
2429
+ // Fallback to legacy collection logic if ToolManager not available
2430
+ logger.warn(`[ResponseModal] ToolManager not available, using legacy tool collection`);
2431
+
2432
+ const availableTools = new Map<string, Tool<TContext, TData>>();
1551
2433
 
1552
2434
  // Add agent-level tools
1553
2435
  this.agent.getTools().forEach((tool) => {
@@ -1556,7 +2438,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1556
2438
 
1557
2439
  // Add route-level tools (these take precedence)
1558
2440
  if (route) {
1559
- route.getTools().forEach((tool: Tool<TContext, TData, unknown[], unknown>) => {
2441
+ route.getTools().forEach((tool: Tool<TContext, TData>) => {
1560
2442
  availableTools.set(tool.id, tool);
1561
2443
  });
1562
2444
  }
@@ -1564,7 +2446,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1564
2446
  // Filter by step-level allowed tools if specified
1565
2447
  if (step?.tools) {
1566
2448
  const allowedToolIds = new Set<string>();
1567
- const stepTools: Tool<TContext, TData, unknown[], unknown>[] = [];
2449
+ const stepTools: Tool<TContext, TData>[] = [];
1568
2450
 
1569
2451
  for (const toolRef of step.tools) {
1570
2452
  if (typeof toolRef === "string") {
@@ -1581,7 +2463,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1581
2463
 
1582
2464
  // If step specifies tools, only include those
1583
2465
  if (allowedToolIds.size > 0) {
1584
- const filteredTools = new Map<string, Tool<TContext, TData, unknown[], unknown>>();
2466
+ const filteredTools = new Map<string, Tool<TContext, TData>>();
1585
2467
  for (const toolId of Array.from(allowedToolIds)) {
1586
2468
  const tool = availableTools.get(toolId);
1587
2469
  if (tool) {
@@ -1615,7 +2497,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1615
2497
  private async executePrepareFinalize(
1616
2498
  prepareOrFinalize:
1617
2499
  | string
1618
- | Tool<TContext, TData, unknown[], unknown>
2500
+ | Tool<TContext, TData>
1619
2501
  | ((context: TContext, data?: Partial<TData>) => void | Promise<void>)
1620
2502
  | undefined,
1621
2503
  context: TContext,
@@ -1630,51 +2512,67 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1630
2512
  await prepareOrFinalize(context, data);
1631
2513
  } else {
1632
2514
  // It's a tool reference - find and execute the tool
1633
- let tool: Tool<TContext, TData, unknown[], unknown> | undefined;
2515
+ let tool: Tool<TContext, TData> | undefined;
1634
2516
 
1635
2517
  if (typeof prepareOrFinalize === "string") {
1636
- // Tool ID - find it in available tools
1637
- const availableTools = new Map<string, Tool<TContext, TData, unknown[], unknown>>();
1638
-
1639
- // Add agent-level tools
1640
- this.agent.getTools().forEach((t) => {
1641
- availableTools.set(t.id, t);
1642
- });
2518
+ // Tool ID - use ToolManager for unified resolution
2519
+ const toolManager = this.getToolManager();
2520
+ if (toolManager) {
2521
+ tool = toolManager.find(prepareOrFinalize, undefined, step, route);
2522
+ } else {
2523
+ // Fallback to legacy resolution if ToolManager not available
2524
+ logger.warn(`[ResponseModal] ToolManager not available, using legacy tool resolution for prepare/finalize: ${prepareOrFinalize}`);
2525
+
2526
+ const availableTools = new Map<string, Tool<TContext, TData>>();
1643
2527
 
1644
- // Add route-level tools
1645
- if (route) {
1646
- route.getTools().forEach((t: Tool<TContext, TData, unknown[], unknown>) => {
2528
+ // Add agent-level tools
2529
+ this.agent.getTools().forEach((t) => {
1647
2530
  availableTools.set(t.id, t);
1648
2531
  });
1649
- }
1650
2532
 
1651
- // Add step-level tools
1652
- if (step?.tools) {
1653
- for (const toolRef of step.tools) {
1654
- if (typeof toolRef === "string") {
1655
- // Keep as is
1656
- } else if (typeof toolRef === 'object' && 'id' in toolRef && toolRef.id) {
1657
- availableTools.set(toolRef.id, toolRef);
2533
+ // Add route-level tools
2534
+ if (route) {
2535
+ route.getTools().forEach((t: Tool<TContext, TData>) => {
2536
+ availableTools.set(t.id, t);
2537
+ });
2538
+ }
2539
+
2540
+ // Add step-level tools
2541
+ if (step?.tools) {
2542
+ for (const toolRef of step.tools) {
2543
+ if (typeof toolRef === "string") {
2544
+ // Keep as is
2545
+ } else if (typeof toolRef === 'object' && 'id' in toolRef && toolRef.id) {
2546
+ availableTools.set(toolRef.id, toolRef);
2547
+ }
1658
2548
  }
1659
2549
  }
1660
- }
1661
2550
 
1662
- tool = availableTools.get(prepareOrFinalize);
2551
+ tool = availableTools.get(prepareOrFinalize);
2552
+ }
1663
2553
  } else {
1664
2554
  // Tool object - use directly
1665
2555
  tool = prepareOrFinalize;
1666
2556
  }
1667
2557
 
1668
2558
  if (tool) {
1669
- const toolExecutor = new ToolExecutor<TContext, TData>();
1670
- const result = await toolExecutor.executeTool({
1671
- tool,
1672
- context,
1673
- updateContext: this.agent.updateContext.bind(this.agent),
1674
- updateData: this.agent.updateCollectedData.bind(this.agent),
1675
- history: [], // Empty history for prepare/finalize
1676
- data,
1677
- });
2559
+ // Use ToolManager for unified tool execution
2560
+ const toolManager = this.getToolManager();
2561
+ let result;
2562
+
2563
+ if (toolManager) {
2564
+ result = await toolManager.executeTool({
2565
+ tool,
2566
+ context,
2567
+ updateContext: this.agent.updateContext.bind(this.agent),
2568
+ updateData: this.agent.updateCollectedData.bind(this.agent),
2569
+ history: [], // Empty history for prepare/finalize
2570
+ data,
2571
+ });
2572
+ } else {
2573
+ // Fallback: execute tool directly if ToolManager not available
2574
+ throw new Error(`ToolManager not available for prepare/finalize tool execution: ${typeof prepareOrFinalize === "string" ? prepareOrFinalize : "inline tool"}`);
2575
+ }
1678
2576
 
1679
2577
  if (!result.success) {
1680
2578
  logger.error(