@falai/agent 0.9.2 → 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 (626) hide show
  1. package/README.md +262 -38
  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/cjs/{src/core → core}/Agent.d.ts +6 -1
  33. package/dist/cjs/core/Agent.d.ts.map +1 -0
  34. package/dist/cjs/{src/core → core}/Agent.js +42 -2
  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 +45 -0
  54. package/dist/cjs/core/ResponseModal.d.ts.map +1 -0
  55. package/dist/cjs/{src/core → core}/ResponseModal.js +686 -66
  56. package/dist/cjs/core/ResponseModal.js.map +1 -0
  57. package/dist/{src → cjs}/core/ResponsePipeline.d.ts +2 -2
  58. package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -0
  59. package/dist/cjs/{src/core → core}/ResponsePipeline.js +13 -6
  60. package/dist/cjs/core/ResponsePipeline.js.map +1 -0
  61. package/dist/{src → cjs}/core/Route.d.ts +34 -5
  62. package/dist/cjs/core/Route.d.ts.map +1 -0
  63. package/dist/cjs/{src/core → core}/Route.js +196 -19
  64. package/dist/cjs/core/Route.js.map +1 -0
  65. package/dist/cjs/{src/core → core}/RoutingEngine.d.ts +30 -5
  66. package/dist/cjs/core/RoutingEngine.d.ts.map +1 -0
  67. package/dist/cjs/{src/core → core}/RoutingEngine.js +330 -80
  68. package/dist/cjs/core/RoutingEngine.js.map +1 -0
  69. package/dist/cjs/core/SessionManager.js.map +1 -0
  70. package/dist/{src → cjs}/core/Step.d.ts +31 -10
  71. package/dist/cjs/core/Step.d.ts.map +1 -0
  72. package/dist/cjs/{src/core → core}/Step.js +105 -10
  73. package/dist/cjs/core/Step.js.map +1 -0
  74. package/dist/cjs/core/ToolManager.js.map +1 -0
  75. package/dist/{src → cjs}/index.d.ts +4 -1
  76. package/dist/cjs/index.d.ts.map +1 -0
  77. package/dist/cjs/{src/index.js → index.js} +12 -1
  78. package/dist/cjs/index.js.map +1 -0
  79. package/dist/cjs/{src/providers → providers}/AnthropicProvider.js +18 -18
  80. package/dist/cjs/providers/AnthropicProvider.js.map +1 -0
  81. package/dist/{src → cjs}/providers/GeminiProvider.d.ts.map +1 -1
  82. package/dist/cjs/{src/providers → providers}/GeminiProvider.js +123 -51
  83. package/dist/cjs/providers/GeminiProvider.js.map +1 -0
  84. package/dist/cjs/{src/providers → providers}/OpenAIProvider.js +19 -19
  85. package/dist/cjs/providers/OpenAIProvider.js.map +1 -0
  86. package/dist/cjs/{src/providers → providers}/OpenRouterProvider.js +19 -19
  87. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -0
  88. package/dist/cjs/providers/index.js.map +1 -0
  89. package/dist/cjs/{src/types → types}/agent.d.ts +11 -3
  90. package/dist/cjs/types/agent.d.ts.map +1 -0
  91. package/dist/cjs/types/agent.js.map +1 -0
  92. package/dist/{src → cjs}/types/ai.js.map +1 -1
  93. package/dist/cjs/types/history.js.map +1 -0
  94. package/dist/cjs/{src/types → types}/index.d.ts +2 -1
  95. package/dist/{src → cjs}/types/index.d.ts.map +1 -1
  96. package/dist/cjs/{src/types → types}/index.js +6 -1
  97. package/dist/cjs/types/index.js.map +1 -0
  98. package/dist/cjs/types/persistence.js.map +1 -0
  99. package/dist/cjs/{src/types → types}/route.d.ts +111 -12
  100. package/dist/cjs/types/route.d.ts.map +1 -0
  101. package/dist/cjs/{src/types → types}/route.js.map +1 -1
  102. package/dist/cjs/types/session.js.map +1 -0
  103. package/dist/cjs/types/template.d.ts +88 -0
  104. package/dist/cjs/types/template.d.ts.map +1 -0
  105. package/dist/cjs/types/tool.js.map +1 -0
  106. package/dist/cjs/utils/clone.js.map +1 -0
  107. package/dist/cjs/utils/condition.d.ts +38 -0
  108. package/dist/cjs/utils/condition.d.ts.map +1 -0
  109. package/dist/cjs/utils/condition.js +168 -0
  110. package/dist/cjs/utils/condition.js.map +1 -0
  111. package/dist/cjs/utils/event.js.map +1 -0
  112. package/dist/cjs/utils/history.js.map +1 -0
  113. package/dist/cjs/utils/id.js.map +1 -0
  114. package/dist/cjs/{src/utils → utils}/index.d.ts +3 -1
  115. package/dist/cjs/utils/index.d.ts.map +1 -0
  116. package/dist/cjs/{src/utils → utils}/index.js +12 -1
  117. package/dist/cjs/utils/index.js.map +1 -0
  118. package/dist/cjs/utils/json.d.ts +16 -0
  119. package/dist/cjs/utils/json.d.ts.map +1 -0
  120. package/dist/cjs/utils/json.js +47 -0
  121. package/dist/cjs/utils/json.js.map +1 -0
  122. package/dist/cjs/utils/logger.js.map +1 -0
  123. package/dist/{src → cjs}/utils/retry.d.ts +0 -3
  124. package/dist/cjs/utils/retry.d.ts.map +1 -0
  125. package/dist/cjs/{src/utils → utils}/retry.js +8 -7
  126. package/dist/cjs/utils/retry.js.map +1 -0
  127. package/dist/cjs/utils/session.js.map +1 -0
  128. package/dist/{src → cjs}/utils/template.d.ts +48 -0
  129. package/dist/cjs/utils/template.d.ts.map +1 -0
  130. package/dist/cjs/{src/utils → utils}/template.js +100 -0
  131. package/dist/cjs/utils/template.js.map +1 -0
  132. package/dist/constants/index.d.ts.map +1 -0
  133. package/dist/constants/index.js.map +1 -0
  134. package/dist/{src/core → core}/Agent.d.ts +6 -1
  135. package/dist/core/Agent.d.ts.map +1 -0
  136. package/dist/{src/core → core}/Agent.js +43 -3
  137. package/dist/core/Agent.js.map +1 -0
  138. package/dist/core/BatchExecutor.d.ts +353 -0
  139. package/dist/core/BatchExecutor.d.ts.map +1 -0
  140. package/dist/core/BatchExecutor.js +837 -0
  141. package/dist/core/BatchExecutor.js.map +1 -0
  142. package/dist/core/BatchPromptBuilder.d.ts +86 -0
  143. package/dist/core/BatchPromptBuilder.d.ts.map +1 -0
  144. package/dist/core/BatchPromptBuilder.js +197 -0
  145. package/dist/core/BatchPromptBuilder.js.map +1 -0
  146. package/dist/core/Events.d.ts.map +1 -0
  147. package/dist/core/Events.js.map +1 -0
  148. package/dist/core/PersistenceManager.d.ts.map +1 -0
  149. package/dist/core/PersistenceManager.js.map +1 -0
  150. package/dist/{cjs/src/core → core}/PromptComposer.d.ts +1 -1
  151. package/dist/core/PromptComposer.d.ts.map +1 -0
  152. package/dist/{src/core → core}/PromptComposer.js +45 -8
  153. package/dist/core/PromptComposer.js.map +1 -0
  154. package/dist/core/ResponseEngine.d.ts.map +1 -0
  155. package/dist/core/ResponseEngine.js +198 -0
  156. package/dist/core/ResponseEngine.js.map +1 -0
  157. package/dist/{cjs/src/core → core}/ResponseModal.d.ts +45 -0
  158. package/dist/core/ResponseModal.d.ts.map +1 -0
  159. package/dist/{src/core → core}/ResponseModal.js +686 -66
  160. package/dist/core/ResponseModal.js.map +1 -0
  161. package/dist/{cjs/src/core → core}/ResponsePipeline.d.ts +2 -2
  162. package/dist/core/ResponsePipeline.d.ts.map +1 -0
  163. package/dist/{src/core → core}/ResponsePipeline.js +13 -6
  164. package/dist/core/ResponsePipeline.js.map +1 -0
  165. package/dist/{cjs/src/core → core}/Route.d.ts +34 -5
  166. package/dist/core/Route.d.ts.map +1 -0
  167. package/dist/{src/core → core}/Route.js +195 -18
  168. package/dist/core/Route.js.map +1 -0
  169. package/dist/{src/core → core}/RoutingEngine.d.ts +30 -5
  170. package/dist/core/RoutingEngine.d.ts.map +1 -0
  171. package/dist/{src/core → core}/RoutingEngine.js +310 -60
  172. package/dist/core/RoutingEngine.js.map +1 -0
  173. package/dist/core/SessionManager.d.ts.map +1 -0
  174. package/dist/core/SessionManager.js.map +1 -0
  175. package/dist/{cjs/src/core → core}/Step.d.ts +31 -10
  176. package/dist/core/Step.d.ts.map +1 -0
  177. package/dist/{src/core → core}/Step.js +104 -9
  178. package/dist/core/Step.js.map +1 -0
  179. package/dist/core/ToolManager.d.ts.map +1 -0
  180. package/dist/core/ToolManager.js.map +1 -0
  181. package/dist/{cjs/src/index.d.ts → index.d.ts} +4 -1
  182. package/dist/index.d.ts.map +1 -0
  183. package/dist/{src/index.js → index.js} +3 -0
  184. package/dist/index.js.map +1 -0
  185. package/dist/providers/AnthropicProvider.d.ts.map +1 -0
  186. package/dist/{src/providers → providers}/AnthropicProvider.js +17 -17
  187. package/dist/providers/AnthropicProvider.js.map +1 -0
  188. package/dist/providers/GeminiProvider.d.ts.map +1 -0
  189. package/dist/{src/providers → providers}/GeminiProvider.js +123 -51
  190. package/dist/providers/GeminiProvider.js.map +1 -0
  191. package/dist/providers/OpenAIProvider.d.ts.map +1 -0
  192. package/dist/{src/providers → providers}/OpenAIProvider.js +18 -18
  193. package/dist/providers/OpenAIProvider.js.map +1 -0
  194. package/dist/providers/OpenRouterProvider.d.ts.map +1 -0
  195. package/dist/{src/providers → providers}/OpenRouterProvider.js +18 -18
  196. package/dist/providers/OpenRouterProvider.js.map +1 -0
  197. package/dist/providers/index.d.ts.map +1 -0
  198. package/dist/providers/index.js.map +1 -0
  199. package/dist/{src/types → types}/agent.d.ts +11 -3
  200. package/dist/types/agent.d.ts.map +1 -0
  201. package/dist/types/agent.js.map +1 -0
  202. package/dist/types/ai.d.ts.map +1 -0
  203. package/dist/types/ai.js.map +1 -0
  204. package/dist/types/history.d.ts.map +1 -0
  205. package/dist/types/history.js.map +1 -0
  206. package/dist/{src/types → types}/index.d.ts +2 -1
  207. package/dist/types/index.d.ts.map +1 -0
  208. package/dist/{src/types → types}/index.js +1 -0
  209. package/dist/types/index.js.map +1 -0
  210. package/dist/types/persistence.d.ts.map +1 -0
  211. package/dist/types/persistence.js.map +1 -0
  212. package/dist/{src/types → types}/route.d.ts +111 -12
  213. package/dist/types/route.d.ts.map +1 -0
  214. package/dist/{src/types → types}/route.js.map +1 -1
  215. package/dist/types/routing.d.ts.map +1 -0
  216. package/dist/{cjs/src/types → types}/routing.js.map +1 -1
  217. package/dist/types/schema.d.ts.map +1 -0
  218. package/dist/{cjs/src/types → types}/schema.js.map +1 -1
  219. package/dist/types/session.d.ts.map +1 -0
  220. package/dist/{src/types → types}/session.js.map +1 -1
  221. package/dist/types/template.d.ts +88 -0
  222. package/dist/types/template.d.ts.map +1 -0
  223. package/dist/{cjs/src/types → types}/template.js.map +1 -1
  224. package/dist/types/tool.d.ts.map +1 -0
  225. package/dist/types/tool.js.map +1 -0
  226. package/dist/utils/clone.d.ts.map +1 -0
  227. package/dist/utils/clone.js.map +1 -0
  228. package/dist/utils/condition.d.ts +38 -0
  229. package/dist/utils/condition.d.ts.map +1 -0
  230. package/dist/utils/condition.js +161 -0
  231. package/dist/utils/condition.js.map +1 -0
  232. package/dist/utils/event.d.ts.map +1 -0
  233. package/dist/utils/event.js.map +1 -0
  234. package/dist/utils/history.d.ts.map +1 -0
  235. package/dist/utils/history.js.map +1 -0
  236. package/dist/utils/id.d.ts.map +1 -0
  237. package/dist/utils/id.js.map +1 -0
  238. package/dist/{src/utils → utils}/index.d.ts +3 -1
  239. package/dist/utils/index.d.ts.map +1 -0
  240. package/dist/{src/utils → utils}/index.js +5 -1
  241. package/dist/utils/index.js.map +1 -0
  242. package/dist/utils/json.d.ts +16 -0
  243. package/dist/utils/json.d.ts.map +1 -0
  244. package/dist/utils/json.js +43 -0
  245. package/dist/utils/json.js.map +1 -0
  246. package/dist/utils/logger.d.ts.map +1 -0
  247. package/dist/utils/logger.js.map +1 -0
  248. package/dist/{cjs/src/utils → utils}/retry.d.ts +0 -3
  249. package/dist/utils/retry.d.ts.map +1 -0
  250. package/dist/{src/utils → utils}/retry.js +5 -4
  251. package/dist/utils/retry.js.map +1 -0
  252. package/dist/utils/session.d.ts.map +1 -0
  253. package/dist/utils/session.js.map +1 -0
  254. package/dist/{cjs/src/utils → utils}/template.d.ts +48 -0
  255. package/dist/utils/template.d.ts.map +1 -0
  256. package/dist/{src/utils → utils}/template.js +98 -0
  257. package/dist/utils/template.js.map +1 -0
  258. package/docs/api/README.md +221 -12
  259. package/docs/api/overview.md +202 -3
  260. package/docs/architecture/data-extraction-flow.md +363 -0
  261. package/docs/architecture/multi-step-execution.md +243 -0
  262. package/docs/core/agent/README.md +120 -5
  263. package/docs/core/agent/session-management.md +1 -1
  264. package/docs/core/ai-integration/prompt-composition.md +135 -0
  265. package/docs/core/ai-integration/response-processing.md +146 -0
  266. package/docs/core/conversation-flows/data-collection.md +143 -0
  267. package/docs/core/conversation-flows/routes.md +2 -2
  268. package/docs/core/conversation-flows/step-transitions.md +132 -0
  269. package/docs/core/conversation-flows/steps.md +112 -0
  270. package/docs/core/error-handling.md +193 -0
  271. package/docs/core/routing/intelligent-routing.md +118 -0
  272. package/docs/guides/getting-started/README.md +284 -3
  273. package/docs/guides/migration/README.md +23 -0
  274. package/docs/guides/migration/flexible-routing-conditions.md +375 -0
  275. package/docs/guides/migration/multi-step-execution.md +303 -0
  276. package/examples/advanced-patterns/knowledge-based-agent.ts +101 -24
  277. package/examples/advanced-patterns/persistent-onboarding.ts +40 -5
  278. package/examples/advanced-patterns/route-lifecycle-hooks.ts +82 -12
  279. package/examples/advanced-patterns/streaming-responses.ts +2 -2
  280. package/examples/ai-providers/anthropic-integration.ts +4 -4
  281. package/examples/ai-providers/openai-integration.ts +1 -1
  282. package/examples/condition-patterns/function-only-conditions.ts +365 -0
  283. package/examples/condition-patterns/mixed-array-conditions.ts +477 -0
  284. package/examples/condition-patterns/route-skipif-patterns.ts +468 -0
  285. package/examples/condition-patterns/step-skipif-patterns.ts +0 -0
  286. package/examples/condition-patterns/string-only-conditions.ts +296 -0
  287. package/examples/conversation-flows/completion-transitions.ts +48 -7
  288. package/examples/core-concepts/basic-agent.ts +54 -33
  289. package/examples/core-concepts/schema-driven-extraction.ts +33 -9
  290. package/examples/core-concepts/session-management.ts +51 -16
  291. package/examples/integrations/database-integration.ts +6 -6
  292. package/examples/integrations/healthcare-integration.ts +10 -10
  293. package/examples/integrations/search-integration.ts +8 -8
  294. package/examples/integrations/server-session-management.ts +8 -8
  295. package/examples/persistence/database-persistence.ts +15 -15
  296. package/examples/persistence/memory-sessions.ts +3 -3
  297. package/examples/persistence/redis-persistence.ts +7 -9
  298. package/examples/tools/data-enrichment-tools.ts +4 -4
  299. package/package.json +6 -4
  300. package/src/adapters/PrismaAdapter.ts +3 -2
  301. package/src/adapters/RedisAdapter.ts +3 -3
  302. package/src/core/Agent.ts +54 -2
  303. package/src/core/BatchExecutor.ts +1156 -0
  304. package/src/core/BatchPromptBuilder.ts +275 -0
  305. package/src/core/PromptComposer.ts +53 -16
  306. package/src/core/ResponseEngine.ts +143 -4
  307. package/src/core/ResponseModal.ts +888 -66
  308. package/src/core/ResponsePipeline.ts +17 -9
  309. package/src/core/Route.ts +223 -22
  310. package/src/core/RoutingEngine.ts +426 -83
  311. package/src/core/Step.ts +144 -16
  312. package/src/index.ts +19 -0
  313. package/src/providers/AnthropicProvider.ts +17 -17
  314. package/src/providers/GeminiProvider.ts +129 -60
  315. package/src/providers/OpenAIProvider.ts +18 -18
  316. package/src/providers/OpenRouterProvider.ts +18 -18
  317. package/src/types/agent.ts +11 -3
  318. package/src/types/index.ts +12 -1
  319. package/src/types/route.ts +131 -12
  320. package/src/types/template.ts +70 -2
  321. package/src/utils/condition.ts +190 -0
  322. package/src/utils/index.ts +12 -0
  323. package/src/utils/json.ts +46 -0
  324. package/src/utils/retry.ts +5 -4
  325. package/src/utils/template.ts +109 -0
  326. package/dist/cjs/src/adapters/MemoryAdapter.d.ts.map +0 -1
  327. package/dist/cjs/src/adapters/MemoryAdapter.js.map +0 -1
  328. package/dist/cjs/src/adapters/MongoAdapter.d.ts.map +0 -1
  329. package/dist/cjs/src/adapters/MongoAdapter.js.map +0 -1
  330. package/dist/cjs/src/adapters/OpenSearchAdapter.d.ts.map +0 -1
  331. package/dist/cjs/src/adapters/OpenSearchAdapter.js.map +0 -1
  332. package/dist/cjs/src/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  333. package/dist/cjs/src/adapters/PostgreSQLAdapter.js.map +0 -1
  334. package/dist/cjs/src/adapters/PrismaAdapter.d.ts.map +0 -1
  335. package/dist/cjs/src/adapters/PrismaAdapter.js.map +0 -1
  336. package/dist/cjs/src/adapters/RedisAdapter.d.ts.map +0 -1
  337. package/dist/cjs/src/adapters/RedisAdapter.js.map +0 -1
  338. package/dist/cjs/src/adapters/SQLiteAdapter.d.ts.map +0 -1
  339. package/dist/cjs/src/adapters/SQLiteAdapter.js.map +0 -1
  340. package/dist/cjs/src/adapters/index.d.ts.map +0 -1
  341. package/dist/cjs/src/adapters/index.js.map +0 -1
  342. package/dist/cjs/src/constants/index.d.ts.map +0 -1
  343. package/dist/cjs/src/constants/index.js.map +0 -1
  344. package/dist/cjs/src/core/Agent.d.ts.map +0 -1
  345. package/dist/cjs/src/core/Agent.js.map +0 -1
  346. package/dist/cjs/src/core/Events.d.ts.map +0 -1
  347. package/dist/cjs/src/core/Events.js.map +0 -1
  348. package/dist/cjs/src/core/PersistenceManager.d.ts.map +0 -1
  349. package/dist/cjs/src/core/PersistenceManager.js.map +0 -1
  350. package/dist/cjs/src/core/PromptComposer.d.ts.map +0 -1
  351. package/dist/cjs/src/core/PromptComposer.js.map +0 -1
  352. package/dist/cjs/src/core/ResponseEngine.d.ts.map +0 -1
  353. package/dist/cjs/src/core/ResponseEngine.js +0 -84
  354. package/dist/cjs/src/core/ResponseEngine.js.map +0 -1
  355. package/dist/cjs/src/core/ResponseModal.d.ts.map +0 -1
  356. package/dist/cjs/src/core/ResponseModal.js.map +0 -1
  357. package/dist/cjs/src/core/ResponsePipeline.d.ts.map +0 -1
  358. package/dist/cjs/src/core/ResponsePipeline.js.map +0 -1
  359. package/dist/cjs/src/core/Route.d.ts.map +0 -1
  360. package/dist/cjs/src/core/Route.js.map +0 -1
  361. package/dist/cjs/src/core/RoutingEngine.d.ts.map +0 -1
  362. package/dist/cjs/src/core/RoutingEngine.js.map +0 -1
  363. package/dist/cjs/src/core/SessionManager.d.ts.map +0 -1
  364. package/dist/cjs/src/core/SessionManager.js.map +0 -1
  365. package/dist/cjs/src/core/Step.d.ts.map +0 -1
  366. package/dist/cjs/src/core/Step.js.map +0 -1
  367. package/dist/cjs/src/core/ToolManager.d.ts.map +0 -1
  368. package/dist/cjs/src/core/ToolManager.js.map +0 -1
  369. package/dist/cjs/src/index.d.ts.map +0 -1
  370. package/dist/cjs/src/index.js.map +0 -1
  371. package/dist/cjs/src/providers/AnthropicProvider.d.ts.map +0 -1
  372. package/dist/cjs/src/providers/AnthropicProvider.js.map +0 -1
  373. package/dist/cjs/src/providers/GeminiProvider.d.ts.map +0 -1
  374. package/dist/cjs/src/providers/GeminiProvider.js.map +0 -1
  375. package/dist/cjs/src/providers/OpenAIProvider.d.ts.map +0 -1
  376. package/dist/cjs/src/providers/OpenAIProvider.js.map +0 -1
  377. package/dist/cjs/src/providers/OpenRouterProvider.d.ts.map +0 -1
  378. package/dist/cjs/src/providers/OpenRouterProvider.js.map +0 -1
  379. package/dist/cjs/src/providers/index.d.ts.map +0 -1
  380. package/dist/cjs/src/providers/index.js.map +0 -1
  381. package/dist/cjs/src/types/agent.d.ts.map +0 -1
  382. package/dist/cjs/src/types/agent.js.map +0 -1
  383. package/dist/cjs/src/types/ai.d.ts.map +0 -1
  384. package/dist/cjs/src/types/ai.js.map +0 -1
  385. package/dist/cjs/src/types/history.d.ts.map +0 -1
  386. package/dist/cjs/src/types/history.js.map +0 -1
  387. package/dist/cjs/src/types/index.d.ts.map +0 -1
  388. package/dist/cjs/src/types/index.js.map +0 -1
  389. package/dist/cjs/src/types/persistence.d.ts.map +0 -1
  390. package/dist/cjs/src/types/persistence.js.map +0 -1
  391. package/dist/cjs/src/types/route.d.ts.map +0 -1
  392. package/dist/cjs/src/types/routing.d.ts.map +0 -1
  393. package/dist/cjs/src/types/schema.d.ts.map +0 -1
  394. package/dist/cjs/src/types/session.d.ts.map +0 -1
  395. package/dist/cjs/src/types/session.js.map +0 -1
  396. package/dist/cjs/src/types/template.d.ts +0 -30
  397. package/dist/cjs/src/types/template.d.ts.map +0 -1
  398. package/dist/cjs/src/types/tool.d.ts.map +0 -1
  399. package/dist/cjs/src/types/tool.js.map +0 -1
  400. package/dist/cjs/src/utils/clone.d.ts.map +0 -1
  401. package/dist/cjs/src/utils/clone.js.map +0 -1
  402. package/dist/cjs/src/utils/event.d.ts.map +0 -1
  403. package/dist/cjs/src/utils/event.js.map +0 -1
  404. package/dist/cjs/src/utils/history.d.ts.map +0 -1
  405. package/dist/cjs/src/utils/history.js.map +0 -1
  406. package/dist/cjs/src/utils/id.d.ts.map +0 -1
  407. package/dist/cjs/src/utils/id.js.map +0 -1
  408. package/dist/cjs/src/utils/index.d.ts.map +0 -1
  409. package/dist/cjs/src/utils/index.js.map +0 -1
  410. package/dist/cjs/src/utils/logger.d.ts.map +0 -1
  411. package/dist/cjs/src/utils/logger.js.map +0 -1
  412. package/dist/cjs/src/utils/retry.d.ts.map +0 -1
  413. package/dist/cjs/src/utils/retry.js.map +0 -1
  414. package/dist/cjs/src/utils/session.d.ts.map +0 -1
  415. package/dist/cjs/src/utils/session.js.map +0 -1
  416. package/dist/cjs/src/utils/template.d.ts.map +0 -1
  417. package/dist/cjs/src/utils/template.js.map +0 -1
  418. package/dist/src/adapters/MemoryAdapter.js.map +0 -1
  419. package/dist/src/adapters/MongoAdapter.js.map +0 -1
  420. package/dist/src/adapters/OpenSearchAdapter.js.map +0 -1
  421. package/dist/src/adapters/PostgreSQLAdapter.js.map +0 -1
  422. package/dist/src/adapters/PrismaAdapter.js.map +0 -1
  423. package/dist/src/adapters/RedisAdapter.js.map +0 -1
  424. package/dist/src/adapters/SQLiteAdapter.js.map +0 -1
  425. package/dist/src/adapters/index.js.map +0 -1
  426. package/dist/src/constants/index.js.map +0 -1
  427. package/dist/src/core/Agent.d.ts.map +0 -1
  428. package/dist/src/core/Agent.js.map +0 -1
  429. package/dist/src/core/Events.js.map +0 -1
  430. package/dist/src/core/PersistenceManager.js.map +0 -1
  431. package/dist/src/core/PromptComposer.d.ts.map +0 -1
  432. package/dist/src/core/PromptComposer.js.map +0 -1
  433. package/dist/src/core/ResponseEngine.js +0 -80
  434. package/dist/src/core/ResponseEngine.js.map +0 -1
  435. package/dist/src/core/ResponseModal.d.ts.map +0 -1
  436. package/dist/src/core/ResponseModal.js.map +0 -1
  437. package/dist/src/core/ResponsePipeline.d.ts.map +0 -1
  438. package/dist/src/core/ResponsePipeline.js.map +0 -1
  439. package/dist/src/core/Route.d.ts.map +0 -1
  440. package/dist/src/core/Route.js.map +0 -1
  441. package/dist/src/core/RoutingEngine.d.ts.map +0 -1
  442. package/dist/src/core/RoutingEngine.js.map +0 -1
  443. package/dist/src/core/SessionManager.js.map +0 -1
  444. package/dist/src/core/Step.d.ts.map +0 -1
  445. package/dist/src/core/Step.js.map +0 -1
  446. package/dist/src/core/ToolManager.js.map +0 -1
  447. package/dist/src/index.d.ts.map +0 -1
  448. package/dist/src/index.js.map +0 -1
  449. package/dist/src/providers/AnthropicProvider.js.map +0 -1
  450. package/dist/src/providers/GeminiProvider.js.map +0 -1
  451. package/dist/src/providers/OpenAIProvider.js.map +0 -1
  452. package/dist/src/providers/OpenRouterProvider.js.map +0 -1
  453. package/dist/src/providers/index.js.map +0 -1
  454. package/dist/src/types/agent.d.ts.map +0 -1
  455. package/dist/src/types/agent.js.map +0 -1
  456. package/dist/src/types/history.js.map +0 -1
  457. package/dist/src/types/index.js.map +0 -1
  458. package/dist/src/types/persistence.js.map +0 -1
  459. package/dist/src/types/route.d.ts.map +0 -1
  460. package/dist/src/types/template.d.ts +0 -30
  461. package/dist/src/types/template.d.ts.map +0 -1
  462. package/dist/src/types/tool.js.map +0 -1
  463. package/dist/src/utils/clone.js.map +0 -1
  464. package/dist/src/utils/event.js.map +0 -1
  465. package/dist/src/utils/history.js.map +0 -1
  466. package/dist/src/utils/id.js.map +0 -1
  467. package/dist/src/utils/index.d.ts.map +0 -1
  468. package/dist/src/utils/index.js.map +0 -1
  469. package/dist/src/utils/logger.js.map +0 -1
  470. package/dist/src/utils/retry.d.ts.map +0 -1
  471. package/dist/src/utils/retry.js.map +0 -1
  472. package/dist/src/utils/session.js.map +0 -1
  473. package/dist/src/utils/template.d.ts.map +0 -1
  474. package/dist/src/utils/template.js.map +0 -1
  475. /package/dist/{cjs/src/adapters → adapters}/MemoryAdapter.d.ts +0 -0
  476. /package/dist/{src/adapters → adapters}/MemoryAdapter.js +0 -0
  477. /package/dist/{cjs/src/adapters → adapters}/MongoAdapter.d.ts +0 -0
  478. /package/dist/{src/adapters → adapters}/MongoAdapter.js +0 -0
  479. /package/dist/{cjs/src/adapters → adapters}/OpenSearchAdapter.d.ts +0 -0
  480. /package/dist/{src/adapters → adapters}/OpenSearchAdapter.js +0 -0
  481. /package/dist/{cjs/src/adapters → adapters}/PostgreSQLAdapter.d.ts +0 -0
  482. /package/dist/{src/adapters → adapters}/PostgreSQLAdapter.js +0 -0
  483. /package/dist/{cjs/src/adapters → adapters}/PrismaAdapter.d.ts +0 -0
  484. /package/dist/{cjs/src/adapters → adapters}/RedisAdapter.d.ts +0 -0
  485. /package/dist/{cjs/src/adapters → adapters}/SQLiteAdapter.d.ts +0 -0
  486. /package/dist/{src/adapters → adapters}/SQLiteAdapter.js +0 -0
  487. /package/dist/{cjs/src/adapters → adapters}/index.d.ts +0 -0
  488. /package/dist/{src/adapters → adapters}/index.js +0 -0
  489. /package/dist/{src → cjs}/adapters/MemoryAdapter.d.ts +0 -0
  490. /package/dist/{src → cjs}/adapters/MemoryAdapter.d.ts.map +0 -0
  491. /package/dist/cjs/{src/adapters → adapters}/MemoryAdapter.js +0 -0
  492. /package/dist/{src → cjs}/adapters/MongoAdapter.d.ts +0 -0
  493. /package/dist/{src → cjs}/adapters/MongoAdapter.d.ts.map +0 -0
  494. /package/dist/cjs/{src/adapters → adapters}/MongoAdapter.js +0 -0
  495. /package/dist/{src → cjs}/adapters/OpenSearchAdapter.d.ts +0 -0
  496. /package/dist/{src → cjs}/adapters/OpenSearchAdapter.d.ts.map +0 -0
  497. /package/dist/cjs/{src/adapters → adapters}/OpenSearchAdapter.js +0 -0
  498. /package/dist/{src → cjs}/adapters/PostgreSQLAdapter.d.ts +0 -0
  499. /package/dist/{src → cjs}/adapters/PostgreSQLAdapter.d.ts.map +0 -0
  500. /package/dist/cjs/{src/adapters → adapters}/PostgreSQLAdapter.js +0 -0
  501. /package/dist/{src → cjs}/adapters/PrismaAdapter.d.ts +0 -0
  502. /package/dist/{src → cjs}/adapters/RedisAdapter.d.ts +0 -0
  503. /package/dist/{src → cjs}/adapters/RedisAdapter.d.ts.map +0 -0
  504. /package/dist/{src → cjs}/adapters/SQLiteAdapter.d.ts +0 -0
  505. /package/dist/{src → cjs}/adapters/SQLiteAdapter.d.ts.map +0 -0
  506. /package/dist/cjs/{src/adapters → adapters}/SQLiteAdapter.js +0 -0
  507. /package/dist/{src → cjs}/adapters/index.d.ts +0 -0
  508. /package/dist/{src → cjs}/adapters/index.d.ts.map +0 -0
  509. /package/dist/cjs/{src/adapters → adapters}/index.js +0 -0
  510. /package/dist/cjs/{src/constants → constants}/index.d.ts +0 -0
  511. /package/dist/{src → cjs}/constants/index.d.ts.map +0 -0
  512. /package/dist/cjs/{src/constants → constants}/index.js +0 -0
  513. /package/dist/cjs/{src/core → core}/Events.d.ts +0 -0
  514. /package/dist/{src → cjs}/core/Events.d.ts.map +0 -0
  515. /package/dist/cjs/{src/core → core}/Events.js +0 -0
  516. /package/dist/cjs/{src/core → core}/PersistenceManager.d.ts +0 -0
  517. /package/dist/{src → cjs}/core/PersistenceManager.d.ts.map +0 -0
  518. /package/dist/cjs/{src/core → core}/PersistenceManager.js +0 -0
  519. /package/dist/cjs/{src/core → core}/ResponseEngine.d.ts +0 -0
  520. /package/dist/cjs/{src/core → core}/SessionManager.d.ts +0 -0
  521. /package/dist/{src → cjs}/core/SessionManager.d.ts.map +0 -0
  522. /package/dist/cjs/{src/core → core}/SessionManager.js +0 -0
  523. /package/dist/cjs/{src/core → core}/ToolManager.d.ts +0 -0
  524. /package/dist/{src → cjs}/core/ToolManager.d.ts.map +0 -0
  525. /package/dist/cjs/{src/core → core}/ToolManager.js +0 -0
  526. /package/dist/cjs/{src/providers → providers}/AnthropicProvider.d.ts +0 -0
  527. /package/dist/{src → cjs}/providers/AnthropicProvider.d.ts.map +0 -0
  528. /package/dist/cjs/{src/providers → providers}/GeminiProvider.d.ts +0 -0
  529. /package/dist/cjs/{src/providers → providers}/OpenAIProvider.d.ts +0 -0
  530. /package/dist/{src → cjs}/providers/OpenAIProvider.d.ts.map +0 -0
  531. /package/dist/cjs/{src/providers → providers}/OpenRouterProvider.d.ts +0 -0
  532. /package/dist/{src → cjs}/providers/OpenRouterProvider.d.ts.map +0 -0
  533. /package/dist/cjs/{src/providers → providers}/index.d.ts +0 -0
  534. /package/dist/{src → cjs}/providers/index.d.ts.map +0 -0
  535. /package/dist/cjs/{src/providers → providers}/index.js +0 -0
  536. /package/dist/cjs/{src/types → types}/agent.js +0 -0
  537. /package/dist/cjs/{src/types → types}/ai.d.ts +0 -0
  538. /package/dist/{src → cjs}/types/ai.d.ts.map +0 -0
  539. /package/dist/cjs/{src/types → types}/ai.js +0 -0
  540. /package/dist/cjs/{src/types → types}/history.d.ts +0 -0
  541. /package/dist/{src → cjs}/types/history.d.ts.map +0 -0
  542. /package/dist/cjs/{src/types → types}/history.js +0 -0
  543. /package/dist/cjs/{src/types → types}/persistence.d.ts +0 -0
  544. /package/dist/{src → cjs}/types/persistence.d.ts.map +0 -0
  545. /package/dist/cjs/{src/types → types}/persistence.js +0 -0
  546. /package/dist/cjs/{src/types → types}/route.js +0 -0
  547. /package/dist/cjs/{src/types → types}/routing.d.ts +0 -0
  548. /package/dist/{src → cjs}/types/routing.d.ts.map +0 -0
  549. /package/dist/cjs/{src/types → types}/routing.js +0 -0
  550. /package/dist/{src → cjs}/types/routing.js.map +0 -0
  551. /package/dist/cjs/{src/types → types}/schema.d.ts +0 -0
  552. /package/dist/{src → cjs}/types/schema.d.ts.map +0 -0
  553. /package/dist/cjs/{src/types → types}/schema.js +0 -0
  554. /package/dist/{src → cjs}/types/schema.js.map +0 -0
  555. /package/dist/cjs/{src/types → types}/session.d.ts +0 -0
  556. /package/dist/{src → cjs}/types/session.d.ts.map +0 -0
  557. /package/dist/cjs/{src/types → types}/session.js +0 -0
  558. /package/dist/cjs/{src/types → types}/template.js +0 -0
  559. /package/dist/{src → cjs}/types/template.js.map +0 -0
  560. /package/dist/cjs/{src/types → types}/tool.d.ts +0 -0
  561. /package/dist/{src → cjs}/types/tool.d.ts.map +0 -0
  562. /package/dist/cjs/{src/types → types}/tool.js +0 -0
  563. /package/dist/cjs/{src/utils → utils}/clone.d.ts +0 -0
  564. /package/dist/{src → cjs}/utils/clone.d.ts.map +0 -0
  565. /package/dist/cjs/{src/utils → utils}/clone.js +0 -0
  566. /package/dist/cjs/{src/utils → utils}/event.d.ts +0 -0
  567. /package/dist/{src → cjs}/utils/event.d.ts.map +0 -0
  568. /package/dist/cjs/{src/utils → utils}/event.js +0 -0
  569. /package/dist/cjs/{src/utils → utils}/history.d.ts +0 -0
  570. /package/dist/{src → cjs}/utils/history.d.ts.map +0 -0
  571. /package/dist/cjs/{src/utils → utils}/history.js +0 -0
  572. /package/dist/cjs/{src/utils → utils}/id.d.ts +0 -0
  573. /package/dist/{src → cjs}/utils/id.d.ts.map +0 -0
  574. /package/dist/cjs/{src/utils → utils}/id.js +0 -0
  575. /package/dist/cjs/{src/utils → utils}/logger.d.ts +0 -0
  576. /package/dist/{src → cjs}/utils/logger.d.ts.map +0 -0
  577. /package/dist/cjs/{src/utils → utils}/logger.js +0 -0
  578. /package/dist/cjs/{src/utils → utils}/session.d.ts +0 -0
  579. /package/dist/{src → cjs}/utils/session.d.ts.map +0 -0
  580. /package/dist/cjs/{src/utils → utils}/session.js +0 -0
  581. /package/dist/{src/constants → constants}/index.d.ts +0 -0
  582. /package/dist/{src/constants → constants}/index.js +0 -0
  583. /package/dist/{src/core → core}/Events.d.ts +0 -0
  584. /package/dist/{src/core → core}/Events.js +0 -0
  585. /package/dist/{src/core → core}/PersistenceManager.d.ts +0 -0
  586. /package/dist/{src/core → core}/PersistenceManager.js +0 -0
  587. /package/dist/{src/core → core}/ResponseEngine.d.ts +0 -0
  588. /package/dist/{src/core → core}/SessionManager.d.ts +0 -0
  589. /package/dist/{src/core → core}/SessionManager.js +0 -0
  590. /package/dist/{src/core → core}/ToolManager.d.ts +0 -0
  591. /package/dist/{src/core → core}/ToolManager.js +0 -0
  592. /package/dist/{src/providers → providers}/AnthropicProvider.d.ts +0 -0
  593. /package/dist/{src/providers → providers}/GeminiProvider.d.ts +0 -0
  594. /package/dist/{src/providers → providers}/OpenAIProvider.d.ts +0 -0
  595. /package/dist/{src/providers → providers}/OpenRouterProvider.d.ts +0 -0
  596. /package/dist/{src/providers → providers}/index.d.ts +0 -0
  597. /package/dist/{src/providers → providers}/index.js +0 -0
  598. /package/dist/{src/types → types}/agent.js +0 -0
  599. /package/dist/{src/types → types}/ai.d.ts +0 -0
  600. /package/dist/{src/types → types}/ai.js +0 -0
  601. /package/dist/{src/types → types}/history.d.ts +0 -0
  602. /package/dist/{src/types → types}/history.js +0 -0
  603. /package/dist/{src/types → types}/persistence.d.ts +0 -0
  604. /package/dist/{src/types → types}/persistence.js +0 -0
  605. /package/dist/{src/types → types}/route.js +0 -0
  606. /package/dist/{src/types → types}/routing.d.ts +0 -0
  607. /package/dist/{src/types → types}/routing.js +0 -0
  608. /package/dist/{src/types → types}/schema.d.ts +0 -0
  609. /package/dist/{src/types → types}/schema.js +0 -0
  610. /package/dist/{src/types → types}/session.d.ts +0 -0
  611. /package/dist/{src/types → types}/session.js +0 -0
  612. /package/dist/{src/types → types}/template.js +0 -0
  613. /package/dist/{src/types → types}/tool.d.ts +0 -0
  614. /package/dist/{src/types → types}/tool.js +0 -0
  615. /package/dist/{src/utils → utils}/clone.d.ts +0 -0
  616. /package/dist/{src/utils → utils}/clone.js +0 -0
  617. /package/dist/{src/utils → utils}/event.d.ts +0 -0
  618. /package/dist/{src/utils → utils}/event.js +0 -0
  619. /package/dist/{src/utils → utils}/history.d.ts +0 -0
  620. /package/dist/{src/utils → utils}/history.js +0 -0
  621. /package/dist/{src/utils → utils}/id.d.ts +0 -0
  622. /package/dist/{src/utils → utils}/id.js +0 -0
  623. /package/dist/{src/utils → utils}/logger.d.ts +0 -0
  624. /package/dist/{src/utils → utils}/logger.js +0 -0
  625. /package/dist/{src/utils → utils}/session.d.ts +0 -0
  626. /package/dist/{src/utils → utils}/session.js +0 -0
@@ -15,6 +15,7 @@ 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";
@@ -22,9 +23,13 @@ import type { Route } from "./Route";
22
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";
29
+ import { createTemplateContext } from "../utils/template";
26
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
@@ -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>,
@@ -157,6 +170,12 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
157
170
  this.agent.updateCollectedData.bind(this.agent),
158
171
  this.getToolManager()
159
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>();
160
179
  }
161
180
 
162
181
  /**
@@ -418,12 +437,16 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
418
437
  }
419
438
 
420
439
  // PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use
440
+ // Also performs pre-extraction and batch determination
421
441
  let routingResult: {
422
442
  selectedRoute?: Route<TContext, TData>;
423
443
  selectedStep?: Step<TContext, TData>;
424
444
  responseDirectives?: string[];
425
445
  session: SessionState<TData>;
426
446
  isRouteComplete: boolean;
447
+ batchSteps?: StepOptions<TContext, TData>[];
448
+ batchStoppedReason?: StoppedReason;
449
+ batchStoppedAtStep?: StepOptions<TContext, TData>;
427
450
  };
428
451
  try {
429
452
  routingResult = await this.handleUnifiedRoutingAndStepSelection({
@@ -444,6 +467,9 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
444
467
  selectedStep: routingResult.selectedStep,
445
468
  responseDirectives: routingResult.responseDirectives,
446
469
  isRouteComplete: routingResult.isRouteComplete,
470
+ batchSteps: routingResult.batchSteps,
471
+ batchStoppedReason: routingResult.batchStoppedReason,
472
+ batchStoppedAtStep: routingResult.batchStoppedAtStep,
447
473
  };
448
474
  } catch (error) {
449
475
  // Re-throw ResponseGenerationError as-is, wrap others
@@ -469,6 +495,12 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
469
495
  responseDirectives?: string[];
470
496
  session: SessionState<TData>;
471
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>;
472
504
  }> {
473
505
  try {
474
506
  // Use the ResponsePipeline for optimized routing and step selection
@@ -480,12 +512,84 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
480
512
  context: params.context,
481
513
  signal: params.signal,
482
514
  });
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
+
483
587
  // Determine next step using pipeline method for consistency
484
- const stepResult = this.responsePipeline.determineNextStep({
588
+ const stepResult = await this.responsePipeline.determineNextStep({
485
589
  selectedRoute: routingResult.selectedRoute,
486
590
  selectedStep: routingResult.selectedStep,
487
- session: routingResult.session,
488
- isRouteComplete: routingResult.isRouteComplete,
591
+ session: updatedSession, // Use updated session with pre-extracted data
592
+ isRouteComplete, // Use updated completion status
489
593
  });
490
594
 
491
595
  return {
@@ -493,13 +597,106 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
493
597
  selectedStep: stepResult.nextStep, // Use the determined next step
494
598
  responseDirectives: routingResult.responseDirectives,
495
599
  session: stepResult.session,
496
- isRouteComplete: routingResult.isRouteComplete,
600
+ isRouteComplete, // Use updated completion status
601
+ batchSteps,
602
+ batchStoppedReason,
603
+ batchStoppedAtStep,
497
604
  };
498
605
  } catch (error) {
499
606
  throw ResponseGenerationError.fromError(error, 'routing_optimization', params);
500
607
  }
501
608
  }
502
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
+
503
700
  /**
504
701
  * Unified response generation for non-streaming responses
505
702
  * @private
@@ -507,7 +704,17 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
507
704
  private async generateUnifiedResponse(
508
705
  responseContext: ResponseContext<TContext, TData>
509
706
  ): Promise<AgentResponse<TData>> {
510
- 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;
511
718
  let session = initialSession;
512
719
 
513
720
  // Get last user message (needed for both route and completion handling)
@@ -517,42 +724,87 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
517
724
 
518
725
  let message: string;
519
726
  let toolCalls: Array<{ toolName: string; arguments: Record<string, unknown> }> | undefined = undefined;
727
+ let executedSteps: StepRef[] | undefined;
728
+ let stoppedReason: StoppedReason | undefined;
520
729
 
521
730
 
522
731
 
523
732
  if (selectedRoute && !isRouteComplete) {
524
- // Handle normal route processing
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`);
525
737
 
526
- const result = await this.processRouteResponse({
527
- selectedRoute,
528
- selectedStep,
529
- responseDirectives,
530
- session,
531
- history,
532
- context: effectiveContext,
533
- lastMessageText,
534
- historyEvents,
535
- signal: responseContext.history ? undefined : undefined, // TODO: Fix signal passing
536
- });
738
+ const batchResult = await this.executeBatchResponse({
739
+ selectedRoute,
740
+ batchSteps,
741
+ responseDirectives,
742
+ session,
743
+ history,
744
+ context: effectiveContext,
745
+ historyEvents,
746
+ });
537
747
 
538
- message = result.message;
539
- toolCalls = result.toolCalls;
540
- session = result.session;
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
+ }
541
782
 
542
783
  } else if (isRouteComplete && selectedRoute) {
543
784
  // Handle route completion
785
+ logger.debug(`[ResponseModal] Generating completion message for route: ${selectedRoute.title}`);
544
786
 
545
- message = await this.handleRouteCompletion({
546
- selectedRoute,
547
- session,
548
- context: effectiveContext,
549
- lastMessageText,
550
- historyEvents,
551
- });
787
+ try {
788
+ message = await this.handleRouteCompletion({
789
+ selectedRoute,
790
+ session,
791
+ context: effectiveContext,
792
+ lastMessageText,
793
+ historyEvents,
794
+ signal: undefined,
795
+ });
552
796
 
553
- // Set step to END_ROUTE marker
554
- session = enterStep(session, END_ROUTE_ID, "Route completed");
555
- logger.debug(`[ResponseModal] Route ${selectedRoute.title} completed. Entered END_ROUTE step.`);
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
+ }
556
808
 
557
809
  } else {
558
810
  // Fallback: No routes defined, generate a simple response
@@ -562,16 +814,291 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
562
814
  context: effectiveContext,
563
815
  session,
564
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;
565
822
  }
566
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
567
828
  return {
568
829
  message,
569
830
  session,
570
831
  toolCalls,
571
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,
572
1023
  };
573
1024
  }
574
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,
1051
+ };
1052
+ }
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
+
575
1102
  /**
576
1103
  * Unified streaming response generation
577
1104
  * @private
@@ -579,7 +1106,17 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
579
1106
  private async *generateUnifiedStreamingResponse(
580
1107
  responseContext: ResponseContext<TContext, TData>
581
1108
  ): AsyncGenerator<AgentResponseStreamChunk<TData>> {
582
- 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;
583
1120
  const session = initialSession;
584
1121
 
585
1122
  // Get last user message (needed for both route and completion handling)
@@ -588,17 +1125,35 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
588
1125
  const lastMessageText = getLastMessageFromHistory(historyEvents);
589
1126
 
590
1127
  if (selectedRoute && !isRouteComplete) {
591
- // Handle normal route processing with streaming
592
- yield* this.processRouteStreamingResponse({
593
- selectedRoute,
594
- selectedStep,
595
- responseDirectives,
596
- session,
597
- history,
598
- context: effectiveContext,
599
- lastMessageText,
600
- historyEvents,
601
- });
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
+ }
602
1157
 
603
1158
  } else if (isRouteComplete && selectedRoute) {
604
1159
  // Handle route completion streaming
@@ -619,6 +1174,148 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
619
1174
  });
620
1175
  }
621
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
+
622
1319
  /**
623
1320
  * Execute prepare function for current step if available
624
1321
  * @private
@@ -698,12 +1395,27 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
698
1395
  if (selectedStep) {
699
1396
  nextStep = selectedStep;
700
1397
  } else {
701
- // 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
702
1407
  const routingEngine = this.agent.getRoutingEngine();
703
- 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
+
704
1416
  if (candidates.length > 0) {
705
1417
  nextStep = candidates[0].step;
706
- 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'}`);
707
1419
  } else {
708
1420
  // Fallback to initial step even if it should be skipped
709
1421
  nextStep = selectedRoute.initialStep;
@@ -752,6 +1464,15 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
752
1464
  let message = result.structured?.message || result.message;
753
1465
  let toolCalls = result.structured?.toolCalls;
754
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
+
755
1476
  // Execute tools with unified loop handling
756
1477
  const toolResult = await this.executeUnifiedToolLoop({
757
1478
  toolCalls,
@@ -800,11 +1521,22 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
800
1521
  if (selectedStep) {
801
1522
  nextStep = selectedStep;
802
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
803
1530
  const routingEngine = this.agent.getRoutingEngine();
804
- 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
+
805
1537
  if (candidates.length > 0) {
806
1538
  nextStep = candidates[0].step;
807
- 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'}`);
808
1540
  } else {
809
1541
  nextStep = selectedRoute.initialStep;
810
1542
  logger.warn(`[ResponseModal] No valid steps found, using initial step: ${nextStep.id}`);
@@ -887,6 +1619,10 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
887
1619
  await this.finalizeSession(session, context);
888
1620
  }
889
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
890
1626
  yield {
891
1627
  delta: chunk.delta,
892
1628
  accumulated: chunk.accumulated,
@@ -894,6 +1630,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
894
1630
  session,
895
1631
  toolCalls,
896
1632
  isRouteComplete: false,
1633
+ executedSteps: chunk.done ? [{ id: nextStep.id, routeId: selectedRoute.id }] : undefined,
1634
+ stoppedReason: chunk.done ? 'needs_input' : undefined,
897
1635
  metadata: chunk.metadata,
898
1636
  structured: chunk.structured,
899
1637
  };
@@ -934,7 +1672,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
934
1672
 
935
1673
  // Execute initial dynamic tool calls
936
1674
  if (toolCalls && toolCalls.length > 0) {
937
- logger.debug(`[ResponseModal] Executing ${toolCalls.length} dynamic tool calls`);
1675
+ logger.debug(`[ResponseModal] Executing ${toolCalls.length} dynamic tool calls:`, toolCalls.map(tc => tc.toolName));
938
1676
 
939
1677
  for (const toolCall of toolCalls) {
940
1678
  const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
@@ -1009,7 +1747,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1009
1747
 
1010
1748
  while (hasToolCalls && toolLoopCount < MAX_TOOL_LOOPS) {
1011
1749
  toolLoopCount++;
1012
- 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`);
1013
1751
 
1014
1752
  // Create tool result events with proper Event format structure
1015
1753
  const toolResultEvents: Event<ToolEventData>[] = [];
@@ -1041,12 +1779,21 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1041
1779
  const updatedHistoryEvents = [...historyEvents, ...toolResultEvents];
1042
1780
 
1043
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
1044
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
+
1045
1792
  const followUpResult = await agentOptions.provider.generateMessage({
1046
- prompt: responsePrompt,
1793
+ prompt: responsePrompt + (toolLoopCount > 1 ? "\n\nProvide a text response to the user based on the tool results." : ""),
1047
1794
  history: updatedHistoryEvents, // Use Event[] for AI provider
1048
1795
  context,
1049
- tools: availableTools,
1796
+ tools: shouldProvideTools ? availableTools : [], // Only provide tools on first iteration
1050
1797
  parameters: responseSchema ? {
1051
1798
  jsonSchema: responseSchema,
1052
1799
  schemaName: "tool_followup",
@@ -1058,6 +1805,14 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1058
1805
  const followUpToolCalls = followUpResult.structured?.toolCalls;
1059
1806
  hasToolCalls = followUpToolCalls && followUpToolCalls.length > 0;
1060
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
+
1061
1816
  if (hasToolCalls) {
1062
1817
  logger.debug(`[ResponseModal] Follow-up call produced ${followUpToolCalls!.length} additional tool calls`);
1063
1818
 
@@ -1136,6 +1891,13 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1136
1891
  logger.warn(`[ResponseModal] Tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`);
1137
1892
  }
1138
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
+
1139
1901
  return {
1140
1902
  session,
1141
1903
  finalToolCalls: toolCalls,
@@ -1162,15 +1924,34 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1162
1924
  let updatedSession = session;
1163
1925
 
1164
1926
  // Extract collected data from final response (only for route-based interactions)
1165
- if (selectedRoute && result.structured && nextStep?.collect) {
1927
+ if (selectedRoute && result.structured) {
1166
1928
  try {
1167
1929
  const collectedData: Record<string, unknown> = {};
1168
1930
  // AgentStructuredResponse extends Record<string, unknown>, so we can safely access properties
1169
1931
  const structuredData = result.structured;
1170
1932
 
1171
- 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) {
1172
1953
  const fieldKey = String(field);
1173
- if (fieldKey in structuredData) {
1954
+ if (fieldKey in structuredData && structuredData[fieldKey] !== undefined && structuredData[fieldKey] !== null) {
1174
1955
  collectedData[fieldKey] = structuredData[fieldKey];
1175
1956
  }
1176
1957
  }
@@ -1244,37 +2025,66 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1244
2025
  prompt: endStepSpec.prompt || "Summarize what was accomplished and confirm completion based on the conversation history and collected data",
1245
2026
  });
1246
2027
 
1247
- // Build response schema for completion
1248
- const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, completionStep, this.agent.getSchema());
1249
- 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
+ }
1250
2053
 
1251
- // Build completion response prompt
1252
2054
  const completionPrompt = await this.responseEngine.buildResponsePrompt({
1253
2055
  route: selectedRoute,
1254
2056
  currentStep: completionStep,
1255
2057
  rules: selectedRoute.getRules(),
1256
2058
  prohibitions: selectedRoute.getProhibitions(),
1257
- directives: undefined, // No directives for completion
1258
- history: historyEvents, // Use Event[] for buildResponsePrompt
1259
- 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,
1260
2067
  agentOptions: this.agent.getAgentOptions(),
1261
- combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
2068
+ combinedGuidelines: alwaysActiveGuidelines, // Only non-conditional guidelines
1262
2069
  combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
1263
2070
  context,
1264
2071
  session,
1265
- agentSchema: this.agent.getSchema(),
2072
+ agentSchema: undefined, // No data collection schema for completion
1266
2073
  });
1267
2074
 
1268
2075
  // Generate completion message using AI provider
1269
2076
  const agentOptions = this.agent.getAgentOptions();
2077
+ logger.debug(`[ResponseModal] Calling AI provider for completion message...`);
2078
+
1270
2079
  const completionResult = await agentOptions.provider.generateMessage({
1271
2080
  prompt: completionPrompt,
1272
- history: historyEvents, // Use Event[] for AI provider
2081
+ history: historyEvents,
1273
2082
  context,
1274
2083
  signal,
1275
- parameters: { jsonSchema: responseSchema, schemaName: "completion_message" },
2084
+ parameters: { jsonSchema: completionSchema, schemaName: "completion_message" },
1276
2085
  });
1277
2086
 
2087
+ logger.debug(`[ResponseModal] AI provider returned completion result`);
1278
2088
  const message = completionResult.structured?.message || completionResult.message;
1279
2089
  logger.debug(`[ResponseModal] Generated completion message for route: ${selectedRoute.title}`);
1280
2090
 
@@ -1333,7 +2143,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1333
2143
 
1334
2144
  // Build response schema for completion
1335
2145
  const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, completionStep, this.agent.getSchema());
1336
- const templateContext = { context, session, history: historyEvents }; // Use Event[] for template context
2146
+ const templateContext = createTemplateContext({ context, session, history: historyEvents }); // Use Event[] for template context
1337
2147
 
1338
2148
  // Build completion response prompt
1339
2149
  const completionPrompt = await this.responseEngine.buildResponsePrompt({
@@ -1401,6 +2211,10 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1401
2211
  await this.finalizeSession(session, context);
1402
2212
  }
1403
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
1404
2218
  yield {
1405
2219
  delta: chunk.delta,
1406
2220
  accumulated: chunk.accumulated,
@@ -1408,6 +2222,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1408
2222
  session,
1409
2223
  toolCalls: undefined,
1410
2224
  isRouteComplete: true,
2225
+ executedSteps: chunk.done ? [] : undefined,
2226
+ stoppedReason: chunk.done ? 'route_complete' : undefined,
1411
2227
  metadata: chunk.metadata,
1412
2228
  structured: chunk.structured,
1413
2229
  };
@@ -1502,6 +2318,10 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1502
2318
  await this.finalizeSession(session, context);
1503
2319
  }
1504
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)
1505
2325
  yield {
1506
2326
  delta: chunk.delta,
1507
2327
  accumulated: chunk.accumulated,
@@ -1509,6 +2329,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1509
2329
  session,
1510
2330
  toolCalls: undefined,
1511
2331
  isRouteComplete: false,
2332
+ executedSteps: chunk.done ? [] : undefined,
2333
+ stoppedReason: undefined,
1512
2334
  metadata: chunk.metadata,
1513
2335
  structured: chunk.structured,
1514
2336
  };