@falai/agent 1.2.8 → 2.0.1

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 (522) hide show
  1. package/README.md +40 -886
  2. package/dist/adapters/MemoryAdapter.js +2 -2
  3. package/dist/adapters/MemoryAdapter.js.map +1 -1
  4. package/dist/adapters/MongoAdapter.js +2 -2
  5. package/dist/adapters/MongoAdapter.js.map +1 -1
  6. package/dist/adapters/OpenSearchAdapter.d.ts.map +1 -1
  7. package/dist/adapters/OpenSearchAdapter.js +9 -7
  8. package/dist/adapters/OpenSearchAdapter.js.map +1 -1
  9. package/dist/adapters/PostgreSQLAdapter.d.ts +14 -0
  10. package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -1
  11. package/dist/adapters/PostgreSQLAdapter.js +25 -9
  12. package/dist/adapters/PostgreSQLAdapter.js.map +1 -1
  13. package/dist/adapters/PrismaAdapter.js +5 -5
  14. package/dist/adapters/PrismaAdapter.js.map +1 -1
  15. package/dist/adapters/RedisAdapter.js +2 -2
  16. package/dist/adapters/RedisAdapter.js.map +1 -1
  17. package/dist/adapters/SQLiteAdapter.d.ts +17 -0
  18. package/dist/adapters/SQLiteAdapter.d.ts.map +1 -1
  19. package/dist/adapters/SQLiteAdapter.js +30 -11
  20. package/dist/adapters/SQLiteAdapter.js.map +1 -1
  21. package/dist/cjs/adapters/MemoryAdapter.js +2 -2
  22. package/dist/cjs/adapters/MemoryAdapter.js.map +1 -1
  23. package/dist/cjs/adapters/MongoAdapter.js +2 -2
  24. package/dist/cjs/adapters/MongoAdapter.js.map +1 -1
  25. package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +1 -1
  26. package/dist/cjs/adapters/OpenSearchAdapter.js +9 -7
  27. package/dist/cjs/adapters/OpenSearchAdapter.js.map +1 -1
  28. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +14 -0
  29. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +1 -1
  30. package/dist/cjs/adapters/PostgreSQLAdapter.js +25 -9
  31. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -1
  32. package/dist/cjs/adapters/PrismaAdapter.js +5 -5
  33. package/dist/cjs/adapters/PrismaAdapter.js.map +1 -1
  34. package/dist/cjs/adapters/RedisAdapter.js +2 -2
  35. package/dist/cjs/adapters/RedisAdapter.js.map +1 -1
  36. package/dist/cjs/adapters/SQLiteAdapter.d.ts +17 -0
  37. package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +1 -1
  38. package/dist/cjs/adapters/SQLiteAdapter.js +30 -11
  39. package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -1
  40. package/dist/cjs/constants/index.d.ts +0 -9
  41. package/dist/cjs/constants/index.d.ts.map +1 -1
  42. package/dist/cjs/constants/index.js +2 -11
  43. package/dist/cjs/constants/index.js.map +1 -1
  44. package/dist/cjs/core/Agent.d.ts +119 -153
  45. package/dist/cjs/core/Agent.d.ts.map +1 -1
  46. package/dist/cjs/core/Agent.js +471 -324
  47. package/dist/cjs/core/Agent.js.map +1 -1
  48. package/dist/cjs/core/AutoChainExecutor.d.ts +107 -0
  49. package/dist/cjs/core/AutoChainExecutor.d.ts.map +1 -0
  50. package/dist/cjs/core/AutoChainExecutor.js +297 -0
  51. package/dist/cjs/core/AutoChainExecutor.js.map +1 -0
  52. package/dist/cjs/core/BranchEvaluator.d.ts +54 -0
  53. package/dist/cjs/core/BranchEvaluator.d.ts.map +1 -0
  54. package/dist/cjs/core/BranchEvaluator.js +130 -0
  55. package/dist/cjs/core/BranchEvaluator.js.map +1 -0
  56. package/dist/cjs/core/DirectiveBus.d.ts +88 -0
  57. package/dist/cjs/core/DirectiveBus.d.ts.map +1 -0
  58. package/dist/cjs/core/DirectiveBus.js +196 -0
  59. package/dist/cjs/core/DirectiveBus.js.map +1 -0
  60. package/dist/cjs/core/DirectiveChainTracker.d.ts +49 -0
  61. package/dist/cjs/core/DirectiveChainTracker.d.ts.map +1 -0
  62. package/dist/cjs/core/DirectiveChainTracker.js +121 -0
  63. package/dist/cjs/core/DirectiveChainTracker.js.map +1 -0
  64. package/dist/cjs/core/Flow.d.ts +186 -0
  65. package/dist/cjs/core/Flow.d.ts.map +1 -0
  66. package/dist/cjs/core/Flow.js +550 -0
  67. package/dist/cjs/core/Flow.js.map +1 -0
  68. package/dist/cjs/core/FlowRouter.d.ts +182 -0
  69. package/dist/cjs/core/FlowRouter.d.ts.map +1 -0
  70. package/dist/cjs/core/{RoutingEngine.js → FlowRouter.js} +323 -306
  71. package/dist/cjs/core/FlowRouter.js.map +1 -0
  72. package/dist/cjs/core/PersistenceManager.d.ts +2 -2
  73. package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
  74. package/dist/cjs/core/PersistenceManager.js +7 -7
  75. package/dist/cjs/core/PersistenceManager.js.map +1 -1
  76. package/dist/cjs/core/PromptComposer.d.ts +21 -8
  77. package/dist/cjs/core/PromptComposer.d.ts.map +1 -1
  78. package/dist/cjs/core/PromptComposer.js +182 -105
  79. package/dist/cjs/core/PromptComposer.js.map +1 -1
  80. package/dist/cjs/core/PromptSectionCache.d.ts +1 -1
  81. package/dist/cjs/core/PromptSectionCache.js +1 -1
  82. package/dist/cjs/core/ResponseEngine.d.ts +18 -8
  83. package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
  84. package/dist/cjs/core/ResponseEngine.js +38 -36
  85. package/dist/cjs/core/ResponseEngine.js.map +1 -1
  86. package/dist/cjs/core/ResponseModal.d.ts +73 -56
  87. package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
  88. package/dist/cjs/core/ResponseModal.js +1191 -1014
  89. package/dist/cjs/core/ResponseModal.js.map +1 -1
  90. package/dist/cjs/core/ResponsePipeline.d.ts +124 -26
  91. package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
  92. package/dist/cjs/core/ResponsePipeline.js +509 -136
  93. package/dist/cjs/core/ResponsePipeline.js.map +1 -1
  94. package/dist/cjs/core/SignalEvaluator.d.ts +86 -0
  95. package/dist/cjs/core/SignalEvaluator.d.ts.map +1 -0
  96. package/dist/cjs/core/SignalEvaluator.js +333 -0
  97. package/dist/cjs/core/SignalEvaluator.js.map +1 -0
  98. package/dist/cjs/core/SignalProcessor.d.ts +152 -0
  99. package/dist/cjs/core/SignalProcessor.d.ts.map +1 -0
  100. package/dist/cjs/core/SignalProcessor.js +562 -0
  101. package/dist/cjs/core/SignalProcessor.js.map +1 -0
  102. package/dist/cjs/core/Step.d.ts +43 -32
  103. package/dist/cjs/core/Step.d.ts.map +1 -1
  104. package/dist/cjs/core/Step.js +221 -126
  105. package/dist/cjs/core/Step.js.map +1 -1
  106. package/dist/cjs/core/StreamingToolExecutor.d.ts +2 -2
  107. package/dist/cjs/core/StreamingToolExecutor.d.ts.map +1 -1
  108. package/dist/cjs/core/StreamingToolExecutor.js.map +1 -1
  109. package/dist/cjs/core/ToolManager.d.ts +44 -13
  110. package/dist/cjs/core/ToolManager.d.ts.map +1 -1
  111. package/dist/cjs/core/ToolManager.js +174 -91
  112. package/dist/cjs/core/ToolManager.js.map +1 -1
  113. package/dist/cjs/core/createAgent.d.ts +35 -0
  114. package/dist/cjs/core/createAgent.d.ts.map +1 -0
  115. package/dist/cjs/core/createAgent.js +39 -0
  116. package/dist/cjs/core/createAgent.js.map +1 -0
  117. package/dist/cjs/core/flow-namespace.d.ts +49 -0
  118. package/dist/cjs/core/flow-namespace.d.ts.map +1 -0
  119. package/dist/cjs/core/flow-namespace.js +171 -0
  120. package/dist/cjs/core/flow-namespace.js.map +1 -0
  121. package/dist/cjs/index.d.ts +11 -14
  122. package/dist/cjs/index.d.ts.map +1 -1
  123. package/dist/cjs/index.js +18 -22
  124. package/dist/cjs/index.js.map +1 -1
  125. package/dist/cjs/providers/AnthropicProvider.d.ts +1 -1
  126. package/dist/cjs/providers/AnthropicProvider.js +1 -1
  127. package/dist/cjs/providers/GeminiProvider.d.ts +1 -1
  128. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  129. package/dist/cjs/providers/GeminiProvider.js +1 -1
  130. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  131. package/dist/cjs/providers/OpenAIProvider.d.ts +1 -1
  132. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
  133. package/dist/cjs/providers/OpenAIProvider.js +1 -1
  134. package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
  135. package/dist/cjs/types/agent.d.ts +183 -54
  136. package/dist/cjs/types/agent.d.ts.map +1 -1
  137. package/dist/cjs/types/agent.js +0 -6
  138. package/dist/cjs/types/agent.js.map +1 -1
  139. package/dist/cjs/types/ai.d.ts +3 -3
  140. package/dist/cjs/types/ai.d.ts.map +1 -1
  141. package/dist/cjs/types/errors.d.ts +15 -0
  142. package/dist/cjs/types/errors.d.ts.map +1 -0
  143. package/dist/cjs/types/errors.js +22 -0
  144. package/dist/cjs/types/errors.js.map +1 -0
  145. package/dist/cjs/types/flow.d.ts +513 -0
  146. package/dist/cjs/types/flow.d.ts.map +1 -0
  147. package/dist/cjs/types/{route.js → flow.js} +2 -2
  148. package/dist/cjs/types/flow.js.map +1 -0
  149. package/dist/cjs/types/index.d.ts +7 -6
  150. package/dist/cjs/types/index.d.ts.map +1 -1
  151. package/dist/cjs/types/index.js +6 -2
  152. package/dist/cjs/types/index.js.map +1 -1
  153. package/dist/cjs/types/persistence.d.ts +11 -7
  154. package/dist/cjs/types/persistence.d.ts.map +1 -1
  155. package/dist/cjs/types/routing.d.ts +1 -1
  156. package/dist/cjs/types/routing.d.ts.map +1 -1
  157. package/dist/cjs/types/session.d.ts +24 -23
  158. package/dist/cjs/types/session.d.ts.map +1 -1
  159. package/dist/cjs/types/signals.d.ts +248 -0
  160. package/dist/cjs/types/signals.d.ts.map +1 -0
  161. package/dist/cjs/types/signals.js +11 -0
  162. package/dist/cjs/types/signals.js.map +1 -0
  163. package/dist/cjs/types/template.d.ts +2 -8
  164. package/dist/cjs/types/template.d.ts.map +1 -1
  165. package/dist/cjs/types/tool.d.ts +36 -29
  166. package/dist/cjs/types/tool.d.ts.map +1 -1
  167. package/dist/cjs/types/tool.js +1 -1
  168. package/dist/cjs/types/tool.js.map +1 -1
  169. package/dist/cjs/utils/condition.d.ts +7 -1
  170. package/dist/cjs/utils/condition.d.ts.map +1 -1
  171. package/dist/cjs/utils/condition.js.map +1 -1
  172. package/dist/cjs/utils/id.d.ts +13 -5
  173. package/dist/cjs/utils/id.d.ts.map +1 -1
  174. package/dist/cjs/utils/id.js +24 -10
  175. package/dist/cjs/utils/id.js.map +1 -1
  176. package/dist/cjs/utils/index.d.ts +2 -2
  177. package/dist/cjs/utils/index.d.ts.map +1 -1
  178. package/dist/cjs/utils/index.js +7 -3
  179. package/dist/cjs/utils/index.js.map +1 -1
  180. package/dist/cjs/utils/session.d.ts +44 -5
  181. package/dist/cjs/utils/session.d.ts.map +1 -1
  182. package/dist/cjs/utils/session.js +197 -38
  183. package/dist/cjs/utils/session.js.map +1 -1
  184. package/dist/constants/index.d.ts +0 -9
  185. package/dist/constants/index.d.ts.map +1 -1
  186. package/dist/constants/index.js +3 -9
  187. package/dist/constants/index.js.map +1 -1
  188. package/dist/core/Agent.d.ts +119 -153
  189. package/dist/core/Agent.d.ts.map +1 -1
  190. package/dist/core/Agent.js +472 -325
  191. package/dist/core/Agent.js.map +1 -1
  192. package/dist/core/AutoChainExecutor.d.ts +107 -0
  193. package/dist/core/AutoChainExecutor.d.ts.map +1 -0
  194. package/dist/core/AutoChainExecutor.js +293 -0
  195. package/dist/core/AutoChainExecutor.js.map +1 -0
  196. package/dist/core/BranchEvaluator.d.ts +54 -0
  197. package/dist/core/BranchEvaluator.d.ts.map +1 -0
  198. package/dist/core/BranchEvaluator.js +126 -0
  199. package/dist/core/BranchEvaluator.js.map +1 -0
  200. package/dist/core/DirectiveBus.d.ts +88 -0
  201. package/dist/core/DirectiveBus.d.ts.map +1 -0
  202. package/dist/core/DirectiveBus.js +192 -0
  203. package/dist/core/DirectiveBus.js.map +1 -0
  204. package/dist/core/DirectiveChainTracker.d.ts +49 -0
  205. package/dist/core/DirectiveChainTracker.d.ts.map +1 -0
  206. package/dist/core/DirectiveChainTracker.js +117 -0
  207. package/dist/core/DirectiveChainTracker.js.map +1 -0
  208. package/dist/core/Flow.d.ts +186 -0
  209. package/dist/core/Flow.d.ts.map +1 -0
  210. package/dist/core/Flow.js +546 -0
  211. package/dist/core/Flow.js.map +1 -0
  212. package/dist/core/FlowRouter.d.ts +182 -0
  213. package/dist/core/FlowRouter.d.ts.map +1 -0
  214. package/dist/core/{RoutingEngine.js → FlowRouter.js} +322 -305
  215. package/dist/core/FlowRouter.js.map +1 -0
  216. package/dist/core/PersistenceManager.d.ts +2 -2
  217. package/dist/core/PersistenceManager.d.ts.map +1 -1
  218. package/dist/core/PersistenceManager.js +7 -7
  219. package/dist/core/PersistenceManager.js.map +1 -1
  220. package/dist/core/PromptComposer.d.ts +21 -8
  221. package/dist/core/PromptComposer.d.ts.map +1 -1
  222. package/dist/core/PromptComposer.js +183 -106
  223. package/dist/core/PromptComposer.js.map +1 -1
  224. package/dist/core/PromptSectionCache.d.ts +1 -1
  225. package/dist/core/PromptSectionCache.js +1 -1
  226. package/dist/core/ResponseEngine.d.ts +18 -8
  227. package/dist/core/ResponseEngine.d.ts.map +1 -1
  228. package/dist/core/ResponseEngine.js +38 -36
  229. package/dist/core/ResponseEngine.js.map +1 -1
  230. package/dist/core/ResponseModal.d.ts +73 -56
  231. package/dist/core/ResponseModal.d.ts.map +1 -1
  232. package/dist/core/ResponseModal.js +1193 -1016
  233. package/dist/core/ResponseModal.js.map +1 -1
  234. package/dist/core/ResponsePipeline.d.ts +124 -26
  235. package/dist/core/ResponsePipeline.d.ts.map +1 -1
  236. package/dist/core/ResponsePipeline.js +509 -137
  237. package/dist/core/ResponsePipeline.js.map +1 -1
  238. package/dist/core/SignalEvaluator.d.ts +86 -0
  239. package/dist/core/SignalEvaluator.d.ts.map +1 -0
  240. package/dist/core/SignalEvaluator.js +326 -0
  241. package/dist/core/SignalEvaluator.js.map +1 -0
  242. package/dist/core/SignalProcessor.d.ts +152 -0
  243. package/dist/core/SignalProcessor.d.ts.map +1 -0
  244. package/dist/core/SignalProcessor.js +555 -0
  245. package/dist/core/SignalProcessor.js.map +1 -0
  246. package/dist/core/Step.d.ts +43 -32
  247. package/dist/core/Step.d.ts.map +1 -1
  248. package/dist/core/Step.js +220 -126
  249. package/dist/core/Step.js.map +1 -1
  250. package/dist/core/StreamingToolExecutor.d.ts +2 -2
  251. package/dist/core/StreamingToolExecutor.d.ts.map +1 -1
  252. package/dist/core/StreamingToolExecutor.js.map +1 -1
  253. package/dist/core/ToolManager.d.ts +44 -13
  254. package/dist/core/ToolManager.d.ts.map +1 -1
  255. package/dist/core/ToolManager.js +174 -91
  256. package/dist/core/ToolManager.js.map +1 -1
  257. package/dist/core/createAgent.d.ts +35 -0
  258. package/dist/core/createAgent.d.ts.map +1 -0
  259. package/dist/core/createAgent.js +36 -0
  260. package/dist/core/createAgent.js.map +1 -0
  261. package/dist/core/flow-namespace.d.ts +49 -0
  262. package/dist/core/flow-namespace.d.ts.map +1 -0
  263. package/dist/core/flow-namespace.js +168 -0
  264. package/dist/core/flow-namespace.js.map +1 -0
  265. package/dist/index.d.ts +11 -14
  266. package/dist/index.d.ts.map +1 -1
  267. package/dist/index.js +9 -12
  268. package/dist/index.js.map +1 -1
  269. package/dist/providers/AnthropicProvider.d.ts +1 -1
  270. package/dist/providers/AnthropicProvider.js +1 -1
  271. package/dist/providers/GeminiProvider.d.ts +1 -1
  272. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  273. package/dist/providers/GeminiProvider.js +1 -1
  274. package/dist/providers/GeminiProvider.js.map +1 -1
  275. package/dist/providers/OpenAIProvider.d.ts +1 -1
  276. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  277. package/dist/providers/OpenAIProvider.js +1 -1
  278. package/dist/providers/OpenAIProvider.js.map +1 -1
  279. package/dist/types/agent.d.ts +183 -54
  280. package/dist/types/agent.d.ts.map +1 -1
  281. package/dist/types/agent.js +0 -6
  282. package/dist/types/agent.js.map +1 -1
  283. package/dist/types/ai.d.ts +3 -3
  284. package/dist/types/ai.d.ts.map +1 -1
  285. package/dist/types/errors.d.ts +15 -0
  286. package/dist/types/errors.d.ts.map +1 -0
  287. package/dist/types/errors.js +18 -0
  288. package/dist/types/errors.js.map +1 -0
  289. package/dist/types/flow.d.ts +513 -0
  290. package/dist/types/flow.d.ts.map +1 -0
  291. package/dist/types/flow.js +5 -0
  292. package/dist/types/flow.js.map +1 -0
  293. package/dist/types/index.d.ts +7 -6
  294. package/dist/types/index.d.ts.map +1 -1
  295. package/dist/types/index.js +4 -1
  296. package/dist/types/index.js.map +1 -1
  297. package/dist/types/persistence.d.ts +11 -7
  298. package/dist/types/persistence.d.ts.map +1 -1
  299. package/dist/types/routing.d.ts +1 -1
  300. package/dist/types/routing.d.ts.map +1 -1
  301. package/dist/types/session.d.ts +24 -23
  302. package/dist/types/session.d.ts.map +1 -1
  303. package/dist/types/signals.d.ts +248 -0
  304. package/dist/types/signals.d.ts.map +1 -0
  305. package/dist/types/signals.js +10 -0
  306. package/dist/types/signals.js.map +1 -0
  307. package/dist/types/template.d.ts +2 -8
  308. package/dist/types/template.d.ts.map +1 -1
  309. package/dist/types/tool.d.ts +36 -29
  310. package/dist/types/tool.d.ts.map +1 -1
  311. package/dist/types/tool.js +1 -1
  312. package/dist/types/tool.js.map +1 -1
  313. package/dist/utils/condition.d.ts +7 -1
  314. package/dist/utils/condition.d.ts.map +1 -1
  315. package/dist/utils/condition.js.map +1 -1
  316. package/dist/utils/id.d.ts +13 -5
  317. package/dist/utils/id.d.ts.map +1 -1
  318. package/dist/utils/id.js +22 -9
  319. package/dist/utils/id.js.map +1 -1
  320. package/dist/utils/index.d.ts +2 -2
  321. package/dist/utils/index.d.ts.map +1 -1
  322. package/dist/utils/index.js +2 -2
  323. package/dist/utils/index.js.map +1 -1
  324. package/dist/utils/session.d.ts +44 -5
  325. package/dist/utils/session.d.ts.map +1 -1
  326. package/dist/utils/session.js +193 -37
  327. package/dist/utils/session.js.map +1 -1
  328. package/docs/README.md +22 -200
  329. package/docs/concepts/architecture.md +281 -0
  330. package/docs/concepts/directives.md +400 -0
  331. package/docs/concepts/pipeline.md +399 -0
  332. package/docs/guides/branching.md +263 -0
  333. package/docs/guides/compaction.md +163 -0
  334. package/docs/guides/conditions.md +167 -0
  335. package/docs/guides/error-handling.md +176 -0
  336. package/docs/guides/flow-control.md +409 -0
  337. package/docs/guides/instructions.md +210 -0
  338. package/docs/guides/persistence.md +182 -0
  339. package/docs/guides/streaming.md +137 -0
  340. package/docs/migration/README.md +14 -0
  341. package/docs/migration/route-to-flow.md +561 -0
  342. package/docs/migration/v1-to-v2.md +909 -0
  343. package/docs/reference/adapters.md +481 -0
  344. package/docs/reference/branches.md +241 -0
  345. package/docs/reference/create-agent.md +186 -0
  346. package/docs/reference/directive.md +243 -0
  347. package/docs/reference/errors.md +122 -0
  348. package/docs/reference/flow.md +238 -0
  349. package/docs/reference/instruction.md +177 -0
  350. package/docs/reference/pre-directive.md +131 -0
  351. package/docs/reference/providers.md +227 -0
  352. package/docs/reference/signals.md +356 -0
  353. package/docs/reference/step.md +339 -0
  354. package/docs/reference/tool.md +269 -0
  355. package/docs/start/01-install.md +81 -0
  356. package/docs/start/02-first-agent.md +196 -0
  357. package/docs/start/03-collect-data.md +222 -0
  358. package/docs/start/04-add-tools.md +276 -0
  359. package/docs/start/05-go-to-production.md +216 -0
  360. package/examples/01-quickstart.ts +20 -0
  361. package/examples/02-data-extraction.ts +90 -0
  362. package/examples/03-tools.ts +136 -0
  363. package/examples/04-instructions.ts +100 -0
  364. package/examples/05-branching.ts +140 -0
  365. package/examples/06-flow-control.ts +103 -0
  366. package/examples/07-streaming.ts +69 -0
  367. package/examples/08-persistence.ts +98 -0
  368. package/examples/09-signals.ts +144 -0
  369. package/examples/tsconfig.json +30 -0
  370. package/package.json +2 -1
  371. package/src/adapters/MemoryAdapter.ts +3 -3
  372. package/src/adapters/MongoAdapter.ts +3 -3
  373. package/src/adapters/OpenSearchAdapter.ts +10 -8
  374. package/src/adapters/PostgreSQLAdapter.ts +26 -10
  375. package/src/adapters/PrismaAdapter.ts +6 -6
  376. package/src/adapters/RedisAdapter.ts +3 -3
  377. package/src/adapters/SQLiteAdapter.ts +31 -12
  378. package/src/constants/index.ts +2 -10
  379. package/src/core/Agent.ts +585 -374
  380. package/src/core/AutoChainExecutor.ts +440 -0
  381. package/src/core/BranchEvaluator.ts +167 -0
  382. package/src/core/DirectiveBus.ts +248 -0
  383. package/src/core/DirectiveChainTracker.ts +144 -0
  384. package/src/core/Flow.ts +666 -0
  385. package/src/core/{RoutingEngine.ts → FlowRouter.ts} +385 -365
  386. package/src/core/PersistenceManager.ts +8 -8
  387. package/src/core/PromptComposer.ts +209 -140
  388. package/src/core/PromptSectionCache.ts +1 -1
  389. package/src/core/ResponseEngine.ts +61 -46
  390. package/src/core/ResponseModal.ts +1453 -1240
  391. package/src/core/ResponsePipeline.ts +655 -175
  392. package/src/core/SignalEvaluator.ts +420 -0
  393. package/src/core/SignalProcessor.ts +723 -0
  394. package/src/core/Step.ts +279 -176
  395. package/src/core/StreamingToolExecutor.ts +4 -4
  396. package/src/core/ToolManager.ts +200 -97
  397. package/src/core/createAgent.ts +40 -0
  398. package/src/core/flow-namespace.ts +219 -0
  399. package/src/index.ts +42 -36
  400. package/src/providers/AnthropicProvider.ts +2 -2
  401. package/src/providers/GeminiProvider.ts +2 -2
  402. package/src/providers/OpenAIProvider.ts +2 -2
  403. package/src/types/agent.ts +182 -53
  404. package/src/types/ai.ts +3 -3
  405. package/src/types/errors.ts +18 -0
  406. package/src/types/flow.ts +590 -0
  407. package/src/types/index.ts +43 -16
  408. package/src/types/persistence.ts +12 -8
  409. package/src/types/routing.ts +1 -1
  410. package/src/types/session.ts +26 -23
  411. package/src/types/signals.ts +321 -0
  412. package/src/types/template.ts +3 -11
  413. package/src/types/tool.ts +50 -42
  414. package/src/utils/condition.ts +13 -4
  415. package/src/utils/id.ts +27 -9
  416. package/src/utils/index.ts +6 -2
  417. package/src/utils/session.ts +238 -42
  418. package/dist/cjs/core/BatchExecutor.d.ts +0 -359
  419. package/dist/cjs/core/BatchExecutor.d.ts.map +0 -1
  420. package/dist/cjs/core/BatchExecutor.js +0 -861
  421. package/dist/cjs/core/BatchExecutor.js.map +0 -1
  422. package/dist/cjs/core/BatchPromptBuilder.d.ts +0 -89
  423. package/dist/cjs/core/BatchPromptBuilder.d.ts.map +0 -1
  424. package/dist/cjs/core/BatchPromptBuilder.js +0 -223
  425. package/dist/cjs/core/BatchPromptBuilder.js.map +0 -1
  426. package/dist/cjs/core/Route.d.ts +0 -180
  427. package/dist/cjs/core/Route.d.ts.map +0 -1
  428. package/dist/cjs/core/Route.js +0 -542
  429. package/dist/cjs/core/Route.js.map +0 -1
  430. package/dist/cjs/core/RoutingEngine.d.ts +0 -185
  431. package/dist/cjs/core/RoutingEngine.d.ts.map +0 -1
  432. package/dist/cjs/core/RoutingEngine.js.map +0 -1
  433. package/dist/cjs/types/route.d.ts +0 -336
  434. package/dist/cjs/types/route.d.ts.map +0 -1
  435. package/dist/cjs/types/route.js.map +0 -1
  436. package/dist/core/BatchExecutor.d.ts +0 -359
  437. package/dist/core/BatchExecutor.d.ts.map +0 -1
  438. package/dist/core/BatchExecutor.js +0 -856
  439. package/dist/core/BatchExecutor.js.map +0 -1
  440. package/dist/core/BatchPromptBuilder.d.ts +0 -89
  441. package/dist/core/BatchPromptBuilder.d.ts.map +0 -1
  442. package/dist/core/BatchPromptBuilder.js +0 -219
  443. package/dist/core/BatchPromptBuilder.js.map +0 -1
  444. package/dist/core/Route.d.ts +0 -180
  445. package/dist/core/Route.d.ts.map +0 -1
  446. package/dist/core/Route.js +0 -538
  447. package/dist/core/Route.js.map +0 -1
  448. package/dist/core/RoutingEngine.d.ts +0 -185
  449. package/dist/core/RoutingEngine.d.ts.map +0 -1
  450. package/dist/core/RoutingEngine.js.map +0 -1
  451. package/dist/types/route.d.ts +0 -336
  452. package/dist/types/route.d.ts.map +0 -1
  453. package/dist/types/route.js +0 -5
  454. package/dist/types/route.js.map +0 -1
  455. package/docs/CONTRIBUTING.md +0 -521
  456. package/docs/api/README.md +0 -3299
  457. package/docs/api/overview.md +0 -1410
  458. package/docs/architecture/data-extraction-flow.md +0 -360
  459. package/docs/architecture/multi-step-execution.md +0 -277
  460. package/docs/core/agent/README.md +0 -938
  461. package/docs/core/agent/context-management.md +0 -796
  462. package/docs/core/agent/rules-and-prohibitions.md +0 -113
  463. package/docs/core/agent/session-management.md +0 -693
  464. package/docs/core/ai-integration/prompt-composition.md +0 -355
  465. package/docs/core/ai-integration/providers.md +0 -515
  466. package/docs/core/ai-integration/response-processing.md +0 -433
  467. package/docs/core/conversation-flows/data-collection.md +0 -772
  468. package/docs/core/conversation-flows/route-dsl.md +0 -509
  469. package/docs/core/conversation-flows/routes.md +0 -249
  470. package/docs/core/conversation-flows/step-transitions.md +0 -731
  471. package/docs/core/conversation-flows/steps.md +0 -268
  472. package/docs/core/error-handling.md +0 -830
  473. package/docs/core/persistence/adapters.md +0 -255
  474. package/docs/core/persistence/session-storage.md +0 -656
  475. package/docs/core/routing/intelligent-routing.md +0 -470
  476. package/docs/core/tools/enhanced-tool.md +0 -186
  477. package/docs/core/tools/streaming-execution.md +0 -161
  478. package/docs/core/tools/tool-definition.md +0 -970
  479. package/docs/core/tools/tool-scoping.md +0 -819
  480. package/docs/guides/advanced-patterns/publishing.md +0 -186
  481. package/docs/guides/context-compaction.md +0 -96
  482. package/docs/guides/error-handling-patterns.md +0 -578
  483. package/docs/guides/getting-started/README.md +0 -795
  484. package/docs/guides/migration/README.md +0 -101
  485. package/docs/guides/migration/flexible-routing-conditions.md +0 -375
  486. package/docs/guides/migration/multi-step-execution.md +0 -393
  487. package/docs/guides/migration/response-modal-refactor.md +0 -518
  488. package/docs/guides/prompt-optimization.md +0 -164
  489. package/examples/advanced-patterns/context-compaction.ts +0 -223
  490. package/examples/advanced-patterns/knowledge-based-agent.ts +0 -735
  491. package/examples/advanced-patterns/persistent-onboarding.ts +0 -728
  492. package/examples/advanced-patterns/route-lifecycle-hooks.ts +0 -556
  493. package/examples/advanced-patterns/streaming-responses.ts +0 -656
  494. package/examples/ai-providers/anthropic-integration.ts +0 -388
  495. package/examples/ai-providers/openai-integration.ts +0 -228
  496. package/examples/condition-patterns/function-only-conditions.ts +0 -365
  497. package/examples/condition-patterns/mixed-array-conditions.ts +0 -477
  498. package/examples/condition-patterns/route-skipif-patterns.ts +0 -468
  499. package/examples/condition-patterns/step-skipif-patterns.ts +0 -0
  500. package/examples/condition-patterns/string-only-conditions.ts +0 -296
  501. package/examples/conversation-flows/completion-transitions.ts +0 -318
  502. package/examples/core-concepts/basic-agent.ts +0 -503
  503. package/examples/core-concepts/modern-streaming-api.ts +0 -309
  504. package/examples/core-concepts/schema-driven-extraction.ts +0 -332
  505. package/examples/core-concepts/session-management.ts +0 -494
  506. package/examples/integrations/database-integration.ts +0 -631
  507. package/examples/integrations/healthcare-integration.ts +0 -595
  508. package/examples/integrations/search-integration.ts +0 -530
  509. package/examples/integrations/server-session-management.ts +0 -307
  510. package/examples/persistence/custom-adapter.ts +0 -526
  511. package/examples/persistence/database-persistence.ts +0 -583
  512. package/examples/persistence/memory-sessions.ts +0 -495
  513. package/examples/persistence/prisma-schema.example.prisma +0 -74
  514. package/examples/persistence/redis-persistence.ts +0 -488
  515. package/examples/tools/basic-tools.ts +0 -765
  516. package/examples/tools/data-enrichment-tools.ts +0 -593
  517. package/examples/tools/enhanced-tool-metadata.ts +0 -268
  518. package/examples/tools/streaming-tool-execution.ts +0 -283
  519. package/src/core/BatchExecutor.ts +0 -1187
  520. package/src/core/BatchPromptBuilder.ts +0 -299
  521. package/src/core/Route.ts +0 -678
  522. package/src/types/route.ts +0 -392
@@ -8,25 +8,52 @@ import type {
8
8
  SessionState,
9
9
  AgentStructuredResponse,
10
10
  Tool,
11
- RouteTransitionConfig,
11
+ Directive,
12
+ PreDirective,
12
13
  } from "../types";
14
+ import type { SignalFiring } from "../types/signals";
15
+ import type { SignalProcessor } from "./SignalProcessor";
13
16
  import type { HistoryItem } from "../types/history";
14
17
  import {
15
18
  createSession,
16
- enterRoute,
17
19
  enterStep,
18
20
  mergeCollected,
21
+ completeCurrentFlow,
19
22
  logger,
20
- render,
21
23
  historyToEvents,
22
24
  serializeToolResult,
23
25
  } from "../utils";
26
+ import { enterFlow } from "../utils/session";
24
27
  import { createTemplateContext } from "../utils/template";
25
- import { Route } from "../core/Route";
26
- import { Step } from "../core/Step";
27
- import { RoutingEngine } from "../core/RoutingEngine";
28
+ import { Flow } from "./Flow";
29
+ import { Step, FlowConfigurationError } from "../core/Step";
30
+ import { FlowRouter } from "./FlowRouter";
28
31
  import type { ToolManager } from "../core/ToolManager";
29
- import { END_ROUTE_ID } from "../constants";
32
+ import { evaluateBranches, createAiConditionEvaluator } from "./BranchEvaluator";
33
+ import { DirectiveChainTracker } from "./DirectiveChainTracker";
34
+ import { DirectiveBus } from "./DirectiveBus";
35
+
36
+ /**
37
+ * Position fields on a Directive that represent a navigation decision.
38
+ * When any of these is set, the directive "wins" the turn's position decision
39
+ * and downstream resolution (branches, linear, AI) must not run.
40
+ */
41
+ const DIRECTIVE_POSITION_FIELDS = ['goTo', 'goToStep', 'complete', 'abort', 'reset'] as const;
42
+
43
+ /**
44
+ * Returns `true` if the given directive has at least one position field set.
45
+ * Used as the guard at the directive-bus / branch-evaluation seam:
46
+ * if the bus produced a winner with a position field, `evaluateStepBranches`
47
+ * (and linear/AI selection) must not run.
48
+ */
49
+ export function hasDirectivePositionField<TContext = unknown, TData = unknown>(
50
+ directive: Directive<TContext, TData> | undefined | null,
51
+ ): boolean {
52
+ if (!directive) return false;
53
+ return DIRECTIVE_POSITION_FIELDS.some(
54
+ (field) => (directive as Record<string, unknown>)[field] !== undefined,
55
+ );
56
+ }
30
57
 
31
58
  export interface ResponsePreparationResult<TContext, TData = unknown> {
32
59
  effectiveContext: TContext;
@@ -34,12 +61,12 @@ export interface ResponsePreparationResult<TContext, TData = unknown> {
34
61
  }
35
62
 
36
63
  export interface RoutingResult<TContext, TData = unknown> {
37
- selectedRoute: Route<TContext, TData> | undefined;
64
+ selectedFlow: Flow<TContext, TData> | undefined;
38
65
  selectedStep: Step<TContext, TData> | undefined;
39
66
  responseDirectives: string[] | undefined;
40
67
  session: SessionState<TData>;
41
- isRouteComplete: boolean;
42
- completedRoutes?: Route<TContext, TData>[];
68
+ isFlowComplete: boolean;
69
+ completedFlows?: Flow<TContext, TData>[];
43
70
  }
44
71
 
45
72
  export interface ToolExecutionResult<TData = unknown> {
@@ -60,11 +87,18 @@ export interface DataCollectionResult<TData = unknown> {
60
87
  * Shared response processing logic between respond() and respondStream() methods
61
88
  */
62
89
  export class ResponsePipeline<TContext = unknown, TData = unknown> {
90
+ /**
91
+ * Per-turn directive chain tracker. Created fresh at the start of each turn
92
+ * via `createChainTracker()`. Used by directive application points to detect
93
+ * infinite redirection loops.
94
+ */
95
+ private _chainTracker: DirectiveChainTracker | undefined;
96
+
63
97
  constructor(
64
98
  private readonly options: AgentOptions<TContext, TData>,
65
- private readonly getRoutes: () => Route<TContext, TData>[],
99
+ private readonly getFlows: () => Flow<TContext, TData>[],
66
100
  private readonly tools: Tool<TContext, TData>[],
67
- private readonly routingEngine: RoutingEngine<TContext, TData>,
101
+ private readonly flowRouter: FlowRouter<TContext, TData>,
68
102
  private readonly updateContext: (
69
103
  updates: Partial<TContext>
70
104
  ) => Promise<void>,
@@ -75,9 +109,108 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
75
109
  private readonly updateCollectedData?: (
76
110
  updates: Partial<TData>
77
111
  ) => Promise<void>,
78
- private readonly toolManager?: ToolManager<TContext, TData>
112
+ private readonly toolManager?: ToolManager<TContext, TData>,
113
+ private readonly signalProcessor?: SignalProcessor<TContext, TData>
79
114
  ) { }
80
115
 
116
+ /**
117
+ * Create a fresh chain tracker for the current turn.
118
+ * Call at the start of each turn; the tracker is discarded at turn end.
119
+ */
120
+ createChainTracker(): DirectiveChainTracker {
121
+ const maxChain = this.options.maxDirectiveChain ?? 10;
122
+ this._chainTracker = new DirectiveChainTracker(maxChain);
123
+ return this._chainTracker;
124
+ }
125
+
126
+ /**
127
+ * Get the current turn's chain tracker (creates one if none exists).
128
+ */
129
+ get chainTracker(): DirectiveChainTracker {
130
+ if (!this._chainTracker) {
131
+ return this.createChainTracker();
132
+ }
133
+ return this._chainTracker;
134
+ }
135
+
136
+ /**
137
+ * Create a fresh DirectiveBus for a new turn.
138
+ * The bus collects directives from hooks, tools, and branches during the turn
139
+ * and merges them at phase boundaries via Algorithm 4.
140
+ */
141
+ createDirectiveBus(): DirectiveBus<TContext, TData> {
142
+ return new DirectiveBus<TContext, TData>();
143
+ }
144
+
145
+ // ──────────────────────────────────────────────────────────────────────────
146
+ // Signal pipeline phases
147
+ // ──────────────────────────────────────────────────────────────────────────
148
+
149
+ /**
150
+ * PRE-SIGNAL PHASE — Evaluates pre/both signals in parallel with routing.
151
+ *
152
+ * Delegates to `signalProcessor.runPreSignalPhase(...)` when configured.
153
+ * When no signal processor is present (zero-cost path), returns a stable
154
+ * empty shape so callers don't need to branch.
155
+ *
156
+ * @requirements 2.1, 2.3, 8.6, 13.3
157
+ */
158
+ async runPreSignalPhase(
159
+ session: SessionState<TData>,
160
+ context: TContext,
161
+ history: Event[],
162
+ ): Promise<{
163
+ firings: SignalFiring<TContext, TData>[];
164
+ updatedSession: SessionState<TData>;
165
+ mergedDirective: PreDirective<TContext, TData> | undefined;
166
+ }> {
167
+ if (!this.signalProcessor) {
168
+ return { firings: [], updatedSession: session, mergedDirective: undefined };
169
+ }
170
+ const result = await this.signalProcessor.runPreSignalPhase({ session, history, context });
171
+ return {
172
+ firings: result.firings,
173
+ updatedSession: result.updatedSession,
174
+ mergedDirective: result.mergedDirective,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * POST-SIGNAL PHASE — Evaluates post/both signals after finalize/onComplete.
180
+ *
181
+ * Delegates to `signalProcessor.runPostSignalPhase(...)` when configured.
182
+ * When no signal processor is present, returns a stable empty shape.
183
+ *
184
+ * Post-phase signals see the complete turn result: assistant message in history,
185
+ * collected data, tool results. Position directives from this phase set
186
+ * `session.pendingDirective` (no mid-turn re-entry per D6 decision).
187
+ *
188
+ * Pre-LLM-only fields (`appendPrompt`, `injectTools`, `halt`) are already
189
+ * dropped inside `runPostSignalPhase` per Phase 4.5 — this seam does NOT
190
+ * re-introduce them.
191
+ *
192
+ * @requirements 9.1, 9.2, 9.3, 9.4
193
+ */
194
+ async runPostSignalPhase(
195
+ session: SessionState<TData>,
196
+ context: TContext,
197
+ history: Event[],
198
+ ): Promise<{
199
+ firings: SignalFiring<TContext, TData>[];
200
+ updatedSession: SessionState<TData>;
201
+ mergedDirective: Directive<TContext, TData> | undefined;
202
+ }> {
203
+ if (!this.signalProcessor) {
204
+ return { firings: [], updatedSession: session, mergedDirective: undefined };
205
+ }
206
+ const result = await this.signalProcessor.runPostSignalPhase({ session, history, context });
207
+ return {
208
+ firings: result.firings,
209
+ updatedSession: result.updatedSession,
210
+ mergedDirective: result.mergedDirective,
211
+ };
212
+ }
213
+
81
214
  /**
82
215
  * Prepare context and session for response generation
83
216
  */
@@ -123,63 +256,169 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
123
256
  }): Promise<RoutingResult<TContext, TData>> {
124
257
  const { session, history, context, signal } = params;
125
258
 
126
- // PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use (combined)
127
- let selectedRoute: Route<TContext, TData> | undefined;
259
+ // PHASE 2: ROUTING + STEP SELECTION - Determine which flow and step to use (combined)
260
+ let selectedFlow: Flow<TContext, TData> | undefined;
128
261
  let responseDirectives: string[] | undefined;
129
262
  let selectedStep: Step<TContext, TData> | undefined;
130
- let isRouteComplete = false;
131
- let completedRoutes: Route<TContext, TData>[] = [];
263
+ let isFlowComplete = false;
264
+ let completedFlows: Flow<TContext, TData>[] = [];
132
265
  let targetSession = session;
133
266
 
134
- // Get routes early since we need them for pending transitions
135
- const routes = this.getRoutes();
267
+ // Get flows early since we need them for pending directives
268
+ const flows = this.getFlows();
136
269
 
137
- // Check for pending transition from previous route completion
138
- if (targetSession.pendingTransition) {
139
- const targetRoute = routes.find(
140
- (r) => r.id === targetSession.pendingTransition?.targetRouteId
270
+ // Check for pending directive from previous flow completion or external dispatch
271
+ if (targetSession.pendingDirective) {
272
+ const directive = targetSession.pendingDirective;
273
+ logger.debug(
274
+ `[ResponseHandler] Applying pending directive at start of turn`
141
275
  );
142
276
 
143
- if (targetRoute) {
144
- logger.debug(
145
- `[ResponseHandler] Auto-transitioning from pending transition to route: ${targetRoute.title}`
146
- );
147
- // Clear pending transition and enter new route
148
- targetSession = {
149
- ...targetSession,
150
- pendingTransition: undefined,
151
- };
152
- targetSession = enterRoute(
153
- targetSession,
154
- targetRoute.id,
155
- targetRoute.title
156
- );
277
+ // Track directive chain depth (Requirement 22.1)
278
+ const tracker = this.chainTracker;
279
+ tracker.record(directive, "pending");
280
+ // If abort (chain breaker), the tracker stops counting; apply normally below
281
+
282
+ // Clear pendingDirective before application (unless complete.next chains another)
283
+ let nextDirective: typeof targetSession.pendingDirective | undefined = undefined;
284
+ if (
285
+ directive.complete &&
286
+ typeof directive.complete === 'object' &&
287
+ directive.complete.next
288
+ ) {
289
+ nextDirective = directive.complete.next;
290
+ }
291
+
292
+ targetSession = {
293
+ ...targetSession,
294
+ pendingDirective: nextDirective,
295
+ };
157
296
 
158
- // Merge initial data if available
159
- if (targetRoute.initialData) {
160
- targetSession = mergeCollected(
161
- targetSession,
162
- targetRoute.initialData
297
+ // Apply the directive: resolve position field to a flow/step
298
+ if (directive.goTo) {
299
+ const flowTarget = typeof directive.goTo === 'string'
300
+ ? directive.goTo
301
+ : directive.goTo.flow;
302
+
303
+ if (flowTarget) {
304
+ const targetFlow = flows.find(
305
+ (r) => r.id === flowTarget || r.title === flowTarget
306
+ );
307
+
308
+ if (targetFlow) {
309
+ logger.debug(
310
+ `[ResponseHandler] Pending directive goTo → flow: ${targetFlow.title}`
311
+ );
312
+ targetSession = enterFlow(
313
+ targetSession,
314
+ targetFlow.id,
315
+ targetFlow.title
316
+ );
317
+
318
+ // Merge initial data if available
319
+ if (targetFlow.initialData) {
320
+ targetSession = mergeCollected(
321
+ targetSession,
322
+ targetFlow.initialData
323
+ );
324
+ }
325
+
326
+ // Merge directive-carried data if present
327
+ if (typeof directive.goTo === 'object' && directive.goTo.data) {
328
+ targetSession = mergeCollected(
329
+ targetSession,
330
+ directive.goTo.data
331
+ );
332
+ }
333
+
334
+ selectedFlow = targetFlow;
335
+
336
+ // If goTo specifies a step, enter it
337
+ if (typeof directive.goTo === 'object' && directive.goTo.step) {
338
+ const stepTarget = directive.goTo.step;
339
+ const targetStep = targetFlow.getStep(stepTarget);
340
+ if (targetStep) {
341
+ targetSession = enterStep(targetSession, targetStep.id, targetStep.description);
342
+ selectedStep = targetStep;
343
+ }
344
+ }
345
+ } else {
346
+ logger.warn(
347
+ `[FlowConfigurationError] Pending directive goTo target not found: flow "${flowTarget}" does not exist. Falling back to normal routing. Fix the goTo reference or remove the pending directive.`
348
+ );
349
+ }
350
+ }
351
+ } else if (directive.goToStep) {
352
+ const stepTarget = typeof directive.goToStep === 'string'
353
+ ? directive.goToStep
354
+ : directive.goToStep.step;
355
+ const flowTarget = typeof directive.goToStep === 'object'
356
+ ? directive.goToStep.flow
357
+ : undefined;
358
+
359
+ if (flowTarget) {
360
+ const targetFlow = flows.find(
361
+ (r) => r.id === flowTarget || r.title === flowTarget
163
362
  );
363
+ if (targetFlow) {
364
+ targetSession = enterFlow(targetSession, targetFlow.id, targetFlow.title);
365
+ selectedFlow = targetFlow;
366
+ const targetStep = targetFlow.getStep(stepTarget);
367
+ if (targetStep) {
368
+ targetSession = enterStep(targetSession, targetStep.id, targetStep.description);
369
+ selectedStep = targetStep;
370
+ }
371
+ }
372
+ } else if (targetSession.currentFlow) {
373
+ // Step within current flow
374
+ const currentFlow = flows.find(r => r.id === targetSession.currentFlow?.id);
375
+ if (currentFlow) {
376
+ selectedFlow = currentFlow;
377
+ const targetStep = currentFlow.getStep(stepTarget);
378
+ if (targetStep) {
379
+ targetSession = enterStep(targetSession, targetStep.id, targetStep.description);
380
+ selectedStep = targetStep;
381
+ }
382
+ }
383
+ }
384
+ } else if (directive.reset) {
385
+ // Reset current flow
386
+ if (targetSession.currentFlow) {
387
+ const currentFlow = flows.find(r => r.id === targetSession.currentFlow?.id);
388
+ if (currentFlow) {
389
+ const resetStep = typeof directive.reset === 'object' && directive.reset.step
390
+ ? directive.reset.step
391
+ : undefined;
392
+ selectedFlow = currentFlow;
393
+ if (resetStep) {
394
+ const targetStep = currentFlow.getStep(resetStep);
395
+ if (targetStep) {
396
+ targetSession = enterStep(targetSession, targetStep.id, targetStep.description);
397
+ selectedStep = targetStep;
398
+ }
399
+ } else {
400
+ // Reset to initial step
401
+ const initialStep = currentFlow.initialStep;
402
+ targetSession = enterStep(targetSession, initialStep.id, initialStep.description);
403
+ selectedStep = initialStep;
404
+ }
405
+ }
164
406
  }
407
+ }
408
+ // For complete/abort, selectedFlow stays undefined → handled downstream
165
409
 
166
- selectedRoute = targetRoute;
167
- } else {
168
- logger.warn(
169
- `[ResponseHandler] Pending transition target route not found: ${targetSession.pendingTransition.targetRouteId}`
170
- );
171
- // Clear invalid transition
172
- targetSession = {
173
- ...targetSession,
174
- pendingTransition: undefined,
175
- };
410
+ // Apply state writes from the directive
411
+ if (directive.dataUpdate) {
412
+ targetSession = mergeCollected(targetSession, directive.dataUpdate);
176
413
  }
414
+
415
+ // Skip FlowRouter.decideFlowAndStep — the directive resolved the position
177
416
  }
178
417
 
179
418
  // If no pending transition or transition handled, do normal routing
180
- if (routes.length > 0 && !selectedRoute) {
181
- const orchestration = await this.routingEngine.decideRouteAndStep({
182
- routes: routes,
419
+ if (flows.length > 0 && !selectedFlow) {
420
+ const orchestration = await this.flowRouter.decideFlowAndStep({
421
+ flows: flows,
183
422
  session: targetSession,
184
423
  history,
185
424
  agentOptions: this.options,
@@ -188,28 +427,28 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
188
427
  signal,
189
428
  });
190
429
 
191
- selectedRoute = orchestration.selectedRoute;
430
+ selectedFlow = orchestration.selectedFlow;
192
431
  selectedStep = orchestration.selectedStep;
193
432
  responseDirectives = orchestration.responseDirectives;
194
433
  targetSession = orchestration.session;
195
- isRouteComplete = orchestration.isRouteComplete || false;
196
- completedRoutes = orchestration.completedRoutes || [];
434
+ isFlowComplete = orchestration.isFlowComplete || false;
435
+ completedFlows = orchestration.completedFlows || [];
197
436
 
198
- // Log if route is complete
199
- if (isRouteComplete) {
437
+ // Log if flow is complete
438
+ if (isFlowComplete) {
200
439
  logger.debug(
201
- `[ResponseHandler] Route complete: all required data collected, END_ROUTE reached`
440
+ `[ResponseHandler] Flow complete: all required data collected or last step reached`
202
441
  );
203
442
  }
204
443
  }
205
444
 
206
445
  return {
207
- selectedRoute,
446
+ selectedFlow,
208
447
  selectedStep,
209
448
  responseDirectives,
210
449
  session: targetSession,
211
- isRouteComplete,
212
- completedRoutes,
450
+ isFlowComplete,
451
+ completedFlows,
213
452
  };
214
453
  }
215
454
 
@@ -217,14 +456,52 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
217
456
  * Determine next step and update session
218
457
  */
219
458
  async determineNextStep(params: {
220
- selectedRoute: Route<TContext, TData> | undefined;
459
+ selectedFlow: Flow<TContext, TData> | undefined;
221
460
  selectedStep: Step<TContext, TData> | undefined;
222
461
  session: SessionState<TData>;
223
- isRouteComplete: boolean;
224
- }): Promise<{ nextStep: Step<TContext, TData> | undefined; session: SessionState<TData> }> {
225
- const { selectedRoute, selectedStep, session, isRouteComplete } = params;
462
+ isFlowComplete: boolean;
463
+ /** Merged directive from the directive bus (pre-LLM + post-LLM phases). */
464
+ busDirective?: Directive<TContext, TData>;
465
+ }): Promise<{ nextStep: Step<TContext, TData> | undefined; session: SessionState<TData>; flowChanged?: Flow<TContext, TData> }> {
466
+ const { selectedFlow, selectedStep, session, isFlowComplete, busDirective } = params;
467
+
468
+ if (!selectedFlow) {
469
+ return { nextStep: undefined, session };
470
+ }
471
+
472
+ // ─── GUARD: directive bus winner with a position field preempts branches ───
473
+ // Resolution precedence (design.md): bus > branches > linear > AI.
474
+ // If the bus produced a directive with a position field (goTo, goToStep,
475
+ // complete, abort, reset), that decision wins the turn. Do NOT evaluate
476
+ // branches or linear/AI selection — the caller applies the bus directive.
477
+ if (hasDirectivePositionField(busDirective)) {
478
+ logger.debug(
479
+ `[ResponseHandler] Directive bus winner has position field — skipping branch evaluation and linear/AI selection`,
480
+ );
481
+ return { nextStep: undefined, session };
482
+ }
483
+
484
+ // STEP 1 (Algorithm 1): branches win over linear chain AND flow completion.
485
+ // Evaluate branches before checking isFlowComplete — a branch can redirect
486
+ // even from the "last" step (which getCandidateStepsWithConditions marks as complete).
487
+ if (!selectedStep) {
488
+ const currentStep = session.currentFlow?.id === selectedFlow.id && session.currentStep
489
+ ? selectedFlow.getStep(session.currentStep.id)
490
+ : undefined;
491
+
492
+ if (currentStep?.branches && currentStep.branches.length > 0) {
493
+ const contextToUse = this.getStoredContext();
494
+ const branchResult = await this.evaluateStepBranches(
495
+ currentStep, selectedFlow, session, contextToUse as TContext
496
+ );
497
+ if (branchResult) {
498
+ return branchResult;
499
+ }
500
+ // undefined → fall through to linear/AI selection or flow completion
501
+ }
502
+ }
226
503
 
227
- if (!selectedRoute || isRouteComplete) {
504
+ if (isFlowComplete) {
228
505
  return { nextStep: undefined, session };
229
506
  }
230
507
 
@@ -234,15 +511,16 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
234
511
  if (selectedStep) {
235
512
  nextStep = selectedStep;
236
513
  } else {
237
- // Determine current step from session if we're already in this route
238
- const currentStep = session.currentRoute?.id === selectedRoute.id && session.currentStep
239
- ? selectedRoute.getStep(session.currentStep.id)
514
+ // Determine current step from session if we're already in this flow
515
+ const currentStep = session.currentFlow?.id === selectedFlow.id && session.currentStep
516
+ ? selectedFlow.getStep(session.currentStep.id)
240
517
  : undefined;
241
518
 
242
519
  const contextToUse = this.getStoredContext();
243
- // Get candidate steps based on current position in the route
244
- const candidates = await this.routingEngine.getCandidateStepsWithConditions(
245
- selectedRoute,
520
+
521
+ // Get candidate steps based on current position in the flow
522
+ const candidates = await this.flowRouter.getCandidateStepsWithConditions(
523
+ selectedFlow,
246
524
  currentStep, // Pass current step instead of undefined to maintain progression
247
525
  createTemplateContext({ data: session.data, session, context: contextToUse })
248
526
  );
@@ -250,13 +528,13 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
250
528
  if (candidates.length > 0) {
251
529
  nextStep = candidates[0].step;
252
530
  logger.debug(
253
- `[ResponseHandler] Using first valid step: ${nextStep.id}${currentStep ? ' (progressing from ' + currentStep.id + ')' : ' for new route'}`
531
+ `[ResponseHandler] Using first valid step: ${nextStep.id}${currentStep ? ' (progressing from ' + currentStep.id + ')' : ' for new flow'}`
254
532
  );
255
533
  } else {
256
534
  // Fallback to initial step even if it should be skipped
257
- nextStep = selectedRoute.initialStep;
535
+ nextStep = selectedFlow.initialStep;
258
536
  logger.warn(
259
- `[ResponseHandler] No valid steps found, using initial step: ${nextStep.id}`
537
+ `[FlowConfigurationError] No valid steps found in flow "${selectedFlow.title}": all candidates were skipped. Falling back to initial step "${nextStep.id}". Review step skip conditions.`
260
538
  );
261
539
  }
262
540
  }
@@ -273,8 +551,8 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
273
551
  );
274
552
  // Stay at current step - don't enter the next one
275
553
  const currentStepId = session.currentStep?.id;
276
- if (currentStepId && selectedRoute) {
277
- const currentStepInstance = selectedRoute.getStep(currentStepId);
554
+ if (currentStepId && selectedFlow) {
555
+ const currentStepInstance = selectedFlow.getStep(currentStepId);
278
556
  if (currentStepInstance) {
279
557
  nextStep = currentStepInstance;
280
558
  }
@@ -294,12 +572,207 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
294
572
  return { nextStep, session: updatedSession };
295
573
  }
296
574
 
575
+ /**
576
+ * Evaluate branches on a step and resolve the `then` value.
577
+ *
578
+ * Resolution rules (Algorithm 1, STEP 1 + then resolution):
579
+ * - String `then` → look up in current flow's step registry first (local step wins).
580
+ * - Not a local step → look up in agent's flow registry; if found, treat as goTo directive.
581
+ * - Neither → throw FlowConfigurationError.
582
+ * - Directive `then` → apply directly (bypass directive bus merge).
583
+ *
584
+ * Returns the resolved { nextStep, session, flowChanged? } or undefined if no branch matched.
585
+ */
586
+ async evaluateStepBranches(
587
+ currentStep: Step<TContext, TData>,
588
+ selectedFlow: Flow<TContext, TData>,
589
+ session: SessionState<TData>,
590
+ context: TContext,
591
+ ): Promise<{ nextStep: Step<TContext, TData> | undefined; session: SessionState<TData>; flowChanged?: Flow<TContext, TData> } | undefined> {
592
+ const history = session.history ? historyToEvents(session.history) : [];
593
+
594
+ // Build the BranchPredicateContext
595
+ const branchCtx = {
596
+ data: session.data,
597
+ context,
598
+ session,
599
+ history,
600
+ };
601
+
602
+ // Create the AI condition evaluator
603
+ const aiEvaluator = createAiConditionEvaluator(
604
+ this.options.provider,
605
+ history,
606
+ context,
607
+ );
608
+
609
+ const result = await evaluateBranches(
610
+ currentStep.branches!,
611
+ branchCtx,
612
+ aiEvaluator,
613
+ );
614
+
615
+ if (result === undefined) {
616
+ return undefined; // No branch matched → fall through to linear/AI selection
617
+ }
618
+
619
+ // Task 4.3: Directive `then` value — apply directly
620
+ if (typeof result === 'object' && result !== null) {
621
+ const directive = result;
622
+ return this.applyBranchDirective(directive, selectedFlow, session);
623
+ }
624
+
625
+ // Task 4.2: String `then` resolution
626
+ // 1. Look up in current flow's step registry first (local step wins)
627
+ const localStep = selectedFlow.getStep(result);
628
+ if (localStep) {
629
+ const updatedSession = enterStep(session, localStep.id, localStep.description);
630
+ logger.debug(`[ResponseHandler] Branch resolved to local step: ${localStep.id}`);
631
+ return { nextStep: localStep, session: updatedSession };
632
+ }
633
+
634
+ // 2. Look up in agent's flow registry
635
+ const flows = this.getFlows();
636
+ const targetFlow = flows.find(f => f.id === result || f.title === result);
637
+ if (targetFlow) {
638
+ // Treat as applyDirective({ goTo: result }) — enter the target flow
639
+ logger.debug(`[ResponseHandler] Branch resolved to flow: ${targetFlow.title}`);
640
+ const updatedSession = enterFlow(session, targetFlow.id, targetFlow.title);
641
+ return { nextStep: undefined, session: updatedSession, flowChanged: targetFlow };
642
+ }
643
+
644
+ // 3. Neither → throw FlowConfigurationError
645
+ throw new FlowConfigurationError(
646
+ `[FlowConfigurationError] Unresolved branch target: "${result}" does not match any step in flow "${selectedFlow.id}" or any flow in the agent. ` +
647
+ `Source: ${selectedFlow.id}.${currentStep.id}. Fix the branch "then" value to reference a valid step id or flow id/title.`
648
+ );
649
+ }
650
+
651
+ /**
652
+ * Apply a Directive returned by a branch entry's `then` value.
653
+ * Branches bypass the directive bus — the Directive is the position decision.
654
+ */
655
+ private applyBranchDirective(
656
+ directive: Directive<TContext, TData>,
657
+ selectedFlow: Flow<TContext, TData>,
658
+ session: SessionState<TData>,
659
+ ): { nextStep: Step<TContext, TData> | undefined; session: SessionState<TData>; flowChanged?: Flow<TContext, TData> } {
660
+ // Track directive chain depth (Requirement 22.1)
661
+ const tracker = this.chainTracker;
662
+ tracker.record(directive, `branch:${selectedFlow.id}`);
663
+
664
+ let updatedSession = session;
665
+
666
+ // Apply state writes first
667
+ if (directive.dataUpdate) {
668
+ updatedSession = mergeCollected(updatedSession, directive.dataUpdate);
669
+ }
670
+
671
+ // Handle position fields
672
+ if (directive.goToStep) {
673
+ const stepTarget = typeof directive.goToStep === 'string'
674
+ ? directive.goToStep
675
+ : directive.goToStep.step;
676
+ const flowTarget = typeof directive.goToStep === 'object'
677
+ ? directive.goToStep.flow
678
+ : undefined;
679
+
680
+ if (flowTarget) {
681
+ // Cross-flow step reference — enter the target flow first
682
+ const flows = this.getFlows();
683
+ const targetFlow = flows.find(f => f.id === flowTarget || f.title === flowTarget);
684
+ if (targetFlow) {
685
+ updatedSession = enterFlow(updatedSession, targetFlow.id, targetFlow.title);
686
+ updatedSession = enterStep(updatedSession, stepTarget);
687
+ // Try to resolve the target step instance for the caller
688
+ const targetStepInstance = targetFlow.getStep(stepTarget);
689
+ logger.debug(`[ResponseHandler] Branch directive goToStep → ${flowTarget}.${stepTarget}`);
690
+ return { nextStep: targetStepInstance || undefined, session: updatedSession, flowChanged: targetFlow };
691
+ }
692
+ throw new FlowConfigurationError(
693
+ `[FlowConfigurationError] Branch directive goToStep targets unknown flow: "${flowTarget}" does not match any flow id or title. ` +
694
+ `Fix the goToStep.flow value or use goTo to target a known flow.`
695
+ );
696
+ }
697
+
698
+ // Local step reference
699
+ const targetStep = selectedFlow.getStep(stepTarget);
700
+ if (targetStep) {
701
+ updatedSession = enterStep(updatedSession, targetStep.id, targetStep.description);
702
+ logger.debug(`[ResponseHandler] Branch directive goToStep → ${targetStep.id}`);
703
+ return { nextStep: targetStep, session: updatedSession };
704
+ }
705
+ throw new FlowConfigurationError(
706
+ `[FlowConfigurationError] Branch directive goToStep targets unknown step: "${stepTarget}" does not exist in flow "${selectedFlow.id}". ` +
707
+ `Fix the goToStep value to reference a valid step id in the current flow.`
708
+ );
709
+ }
710
+
711
+ if (directive.goTo) {
712
+ const flowTarget = typeof directive.goTo === 'string'
713
+ ? directive.goTo
714
+ : directive.goTo.flow ?? directive.goTo.step;
715
+
716
+ if (flowTarget) {
717
+ const flows = this.getFlows();
718
+ const targetFlow = flows.find(f => f.id === flowTarget || f.title === flowTarget);
719
+ if (targetFlow) {
720
+ updatedSession = enterFlow(updatedSession, targetFlow.id, targetFlow.title);
721
+
722
+ // If goTo is an object with a step field, enter that step too
723
+ if (typeof directive.goTo === 'object' && directive.goTo.step) {
724
+ updatedSession = enterStep(updatedSession, directive.goTo.step);
725
+ }
726
+
727
+ logger.debug(`[ResponseHandler] Branch directive goTo → ${targetFlow.title}`);
728
+ return { nextStep: undefined, session: updatedSession, flowChanged: targetFlow };
729
+ }
730
+ throw new FlowConfigurationError(
731
+ `[FlowConfigurationError] Branch directive goTo targets unknown flow: "${flowTarget}" does not match any flow id or title. ` +
732
+ `Fix the goTo value to reference a valid flow.`
733
+ );
734
+ }
735
+ }
736
+
737
+ if (directive.complete) {
738
+ logger.debug(`[ResponseHandler] Branch directive complete`);
739
+ return { nextStep: undefined, session: updatedSession };
740
+ }
741
+
742
+ if (directive.abort) {
743
+ logger.debug(`[ResponseHandler] Branch directive abort`);
744
+ return { nextStep: undefined, session: updatedSession };
745
+ }
746
+
747
+ if (directive.reset) {
748
+ const resetStep = typeof directive.reset === 'object' && directive.reset.step
749
+ ? directive.reset.step
750
+ : undefined;
751
+ if (resetStep) {
752
+ const targetStep = selectedFlow.getStep(resetStep);
753
+ if (targetStep) {
754
+ updatedSession = enterStep(updatedSession, targetStep.id, targetStep.description);
755
+ return { nextStep: targetStep, session: updatedSession };
756
+ }
757
+ }
758
+ // Reset to initial step
759
+ const initialStep = selectedFlow.initialStep;
760
+ updatedSession = enterStep(updatedSession, initialStep.id, initialStep.description);
761
+ logger.debug(`[ResponseHandler] Branch directive reset → ${initialStep.id}`);
762
+ return { nextStep: initialStep, session: updatedSession };
763
+ }
764
+
765
+ // Directive with only non-position fields (e.g., just dataUpdate/contextUpdate/reply)
766
+ // No position change — fall through to linear selection
767
+ return { nextStep: undefined, session: updatedSession };
768
+ }
769
+
297
770
  /**
298
771
  * Execute tool calls and handle results
299
772
  */
300
773
  async executeToolCalls(params: {
301
774
  toolCalls: Array<{ toolName: string; arguments: Record<string, unknown> }>;
302
- selectedRoute?: Route<TContext, TData>;
775
+ selectedFlow?: Flow<TContext, TData>;
303
776
  context: TContext;
304
777
  session: SessionState<TData>;
305
778
  history: Event[];
@@ -307,7 +780,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
307
780
  }): Promise<ToolExecutionResult<TData>> {
308
781
  const {
309
782
  toolCalls,
310
- selectedRoute,
783
+ selectedFlow,
311
784
  context,
312
785
  session,
313
786
  history,
@@ -331,9 +804,9 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
331
804
  const toolResults = new Map<string, string>();
332
805
 
333
806
  for (const toolCall of toolCalls) {
334
- const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
807
+ const tool = this.findAvailableTool(toolCall.toolName, selectedFlow);
335
808
  if (!tool) {
336
- logger.warn(`[ResponseHandler] Tool not found: ${toolCall.toolName}`);
809
+ logger.warn(`[ToolExecutionError] Tool not found: "${toolCall.toolName}" is not registered in any scope (transient, step, flow, or agent). Skipping this tool call. Register the tool or check the tool name.`);
337
810
  continue;
338
811
  }
339
812
 
@@ -377,7 +850,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
377
850
  }
378
851
 
379
852
  logger.debug(
380
- `[ResponseHandler] Executed ${isStreaming ? "streaming " : ""}tool: ${String(tool.id || tool.name || 'unknown')
853
+ `[ResponseHandler] Executed ${isStreaming ? "streaming " : ""}tool: ${String(tool.id || tool.id || 'unknown')
381
854
  } (success: ${result.success})`
382
855
  );
383
856
  }
@@ -396,7 +869,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
396
869
  initialToolCalls:
397
870
  | Array<{ toolName: string; arguments: Record<string, unknown> }>
398
871
  | undefined;
399
- selectedRoute?: Route<TContext, TData>;
872
+ selectedFlow?: Flow<TContext, TData>;
400
873
  nextStep: Step<TContext, TData>;
401
874
  responsePrompt: string;
402
875
  history: HistoryItem[];
@@ -407,7 +880,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
407
880
  }): Promise<ToolExecutionResult<TData>> {
408
881
  const {
409
882
  initialToolCalls,
410
- selectedRoute,
883
+ selectedFlow,
411
884
  nextStep,
412
885
  responsePrompt,
413
886
  history,
@@ -435,7 +908,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
435
908
  // Add tool execution results to history so AI knows what happened
436
909
  const toolResultItems: HistoryItem[] = [];
437
910
  for (const toolCall of currentToolCalls || []) {
438
- const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
911
+ const tool = this.findAvailableTool(toolCall.toolName, selectedFlow);
439
912
  if (tool) {
440
913
  toolResultItems.push({
441
914
  role: "assistant" as const,
@@ -463,7 +936,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
463
936
  prompt: responsePrompt,
464
937
  history: updatedHistory,
465
938
  context,
466
- tools: this.collectAvailableTools(selectedRoute, nextStep),
939
+ tools: this.collectAvailableTools(selectedFlow, nextStep),
467
940
  parameters: {
468
941
  jsonSchema: responseSchema,
469
942
  schemaName: isStreaming ? "tool_followup_streaming" : "tool_followup",
@@ -483,7 +956,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
483
956
  // Execute the follow-up tool calls
484
957
  const toolResult = await this.executeToolCalls({
485
958
  toolCalls: followUpToolCalls!,
486
- selectedRoute,
959
+ selectedFlow,
487
960
  context,
488
961
  session: currentSession,
489
962
  history: historyToEvents(updatedHistory),
@@ -506,8 +979,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
506
979
 
507
980
  if (toolLoopCount >= MAX_TOOL_LOOPS) {
508
981
  logger.warn(
509
- `[ResponseHandler] ${isStreaming ? "Streaming " : ""
510
- }Tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`
982
+ `[ResponseGenerationError] ${isStreaming ? "Streaming t" : "T"}ool loop limit reached: ${toolLoopCount} iterations hit the cap (${MAX_TOOL_LOOPS}). Stopping tool execution. Increase MAX_TOOL_LOOPS or reduce recursive tool calls.`
511
983
  );
512
984
  }
513
985
 
@@ -580,66 +1052,74 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
580
1052
  }
581
1053
 
582
1054
  /**
583
- * Handle route completion logic
1055
+ * Handle flow completion: pure state transition.
1056
+ *
1057
+ * Releases the session to idle (`currentFlow`/`currentStep` cleared),
1058
+ * marks the active `flowHistory` entry completed, and (when `onComplete`
1059
+ * is set) wires `pendingDirective` for the next turn. The framework
1060
+ * emits no message of its own at the completion boundary; callers that
1061
+ * need closing copy should add a final interactive step.
584
1062
  */
585
- async handleRouteCompletion(params: {
586
- selectedRoute: Route<TContext, TData>;
1063
+ async handleFlowCompletion(params: {
1064
+ selectedFlow: Flow<TContext, TData>;
587
1065
  session: SessionState<TData>;
588
1066
  context: TContext;
589
1067
  history: Event[];
590
1068
  }): Promise<{ session: SessionState<TData>; hasTransition: boolean }> {
591
- const { selectedRoute, session, context, history } = params;
1069
+ const { selectedFlow, session, context } = params;
592
1070
 
593
1071
  // Check for onComplete transition
594
- const transitionConfig = await selectedRoute.evaluateOnComplete(
1072
+ const transitionConfig = await selectedFlow.evaluateOnComplete(
595
1073
  { data: session.data },
596
1074
  context
597
1075
  );
598
1076
 
1077
+ // Release to idle. When the flow is reentrant, scrub its owned
1078
+ // fields so a future re-selection doesn't short-circuit on stale data.
1079
+ const ownedFields = selectedFlow.reentrant
1080
+ ? [
1081
+ ...(selectedFlow.requiredFields ?? []),
1082
+ ...(selectedFlow.optionalFields ?? []),
1083
+ ]
1084
+ : undefined;
1085
+ let updatedSession = completeCurrentFlow(session, {
1086
+ clearOwnedFields: ownedFields,
1087
+ });
1088
+
599
1089
  if (transitionConfig) {
600
- // Find target route by ID or title
601
- const targetRoute = this.getRoutes().find(
602
- (r) =>
603
- r.id === transitionConfig.nextStep ||
604
- r.title === transitionConfig.nextStep
605
- );
1090
+ // Extract target from directive's goTo field
1091
+ const goToTarget = typeof transitionConfig.goTo === 'string'
1092
+ ? transitionConfig.goTo
1093
+ : transitionConfig.goTo?.flow;
606
1094
 
607
- if (targetRoute) {
608
- const templateContext = createTemplateContext({
609
- context,
610
- session,
611
- history,
612
- });
613
- const renderedCondition: string =
614
- (await render(transitionConfig.condition, templateContext)) ||
615
- (typeof transitionConfig.condition === "string"
616
- ? transitionConfig.condition
617
- : "");
618
-
619
- // Set pending transition in session
620
- const updatedSession = {
621
- ...session,
622
- pendingTransition: {
623
- targetRouteId: targetRoute.id,
624
- condition: renderedCondition,
625
- reason: "route_complete" as const,
1095
+ // Find target flow by ID or title
1096
+ const targetFlow = goToTarget ? this.getFlows().find(
1097
+ (r) =>
1098
+ r.id === goToTarget ||
1099
+ r.title === goToTarget
1100
+ ) : undefined;
1101
+
1102
+ if (targetFlow) {
1103
+ updatedSession = {
1104
+ ...updatedSession,
1105
+ pendingDirective: {
1106
+ goTo: targetFlow.id,
626
1107
  },
627
1108
  };
628
1109
  logger.debug(
629
- `[ResponseHandler] Route ${selectedRoute.title} completed with pending transition to: ${targetRoute.title}`
1110
+ `[ResponseHandler] Flow ${selectedFlow.title} completed with pending directive to: ${targetFlow.title}`
630
1111
  );
631
1112
  return { session: updatedSession, hasTransition: true };
632
- } else {
1113
+ } else if (goToTarget) {
633
1114
  logger.warn(
634
- `[ResponseHandler] Route ${selectedRoute.title} completed but target route not found: ${transitionConfig.nextStep}`
1115
+ `[FlowConfigurationError] onComplete target not found: flow "${selectedFlow.title}" completed but onComplete target "${goToTarget}" does not match any flow. ` +
1116
+ `Fix the onComplete value to reference an existing flow id/title, or remove onComplete to release the session to idle.`
635
1117
  );
636
1118
  }
637
1119
  }
638
1120
 
639
- // Set step to END_ROUTE marker
640
- const updatedSession = enterStep(session, END_ROUTE_ID, "Route completed");
641
1121
  logger.debug(
642
- `[ResponseHandler] Route ${selectedRoute.title} completed. Entered END_ROUTE step.`
1122
+ `[ResponseHandler] Flow ${selectedFlow.title} completed; session released to idle.`
643
1123
  );
644
1124
 
645
1125
  return { session: updatedSession, hasTransition: false };
@@ -647,46 +1127,46 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
647
1127
 
648
1128
 
649
1129
  /**
650
- * Find an available tool by name for the given route using ToolManager
1130
+ * Find an available tool by name for the given flow using ToolManager
651
1131
  * Delegates to ToolManager for unified tool resolution
652
1132
  */
653
1133
  private findAvailableTool(
654
1134
  toolName: string,
655
- route?: Route<TContext, TData>
1135
+ flow?: Flow<TContext, TData>
656
1136
  ): Tool<TContext, TData> | undefined {
657
1137
  // Use ToolManager for unified tool resolution if available
658
1138
  if (this.toolManager) {
659
- return this.toolManager.find(toolName, undefined, undefined, route);
1139
+ return this.toolManager.find(toolName, undefined, undefined, flow);
660
1140
  }
661
1141
 
662
1142
  // Fallback to legacy resolution if ToolManager not available
663
1143
  logger.warn(`[ResponsePipeline] ToolManager not available, using legacy tool resolution for: ${toolName}`);
664
1144
 
665
- // Check route-level tools first (if route provided)
666
- if (route) {
667
- const routeTool = route
1145
+ // Check flow-level tools first (if flow provided)
1146
+ if (flow) {
1147
+ const flowTool = flow
668
1148
  .getTools()
669
- .find((tool) => tool.id === toolName || tool.name === toolName);
670
- if (routeTool) return routeTool;
1149
+ .find((tool) => tool.id === toolName || tool.id === toolName);
1150
+ if (flowTool) return flowTool;
671
1151
  }
672
1152
 
673
1153
  // Fall back to agent-level tools
674
1154
  return this.tools.find(
675
- (tool) => tool.id === toolName || tool.name === toolName
1155
+ (tool) => tool.id === toolName || tool.id === toolName
676
1156
  );
677
1157
  }
678
1158
 
679
1159
  /**
680
- * Collect all available tools for the given route and step context using ToolManager
1160
+ * Collect all available tools for the given flow and step context using ToolManager
681
1161
  * Delegates to ToolManager for unified tool resolution and deduplication
682
1162
  */
683
1163
  private collectAvailableTools(
684
- route?: Route<TContext, TData>,
1164
+ flow?: Flow<TContext, TData>,
685
1165
  step?: Step<TContext, TData>
686
1166
  ): Array<{ id: string; description?: string; parameters?: unknown }> {
687
1167
  // Use ToolManager for unified tool collection if available
688
1168
  if (this.toolManager) {
689
- const availableTools = this.toolManager.getAvailable(undefined, step, route);
1169
+ const availableTools = this.toolManager.getAvailable(undefined, step, flow);
690
1170
  return availableTools.map((tool) => ({
691
1171
  id: tool.id,
692
1172
  description: tool.description,
@@ -707,9 +1187,9 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
707
1187
  availableTools.set(tool.id, tool);
708
1188
  });
709
1189
 
710
- // Add route-level tools (these take precedence)
711
- if (route) {
712
- route.getTools().forEach((tool) => {
1190
+ // Add flow-level tools (these take precedence)
1191
+ if (flow) {
1192
+ flow.getTools().forEach((tool) => {
713
1193
  availableTools.set(tool.id, tool);
714
1194
  });
715
1195
  }
@@ -798,63 +1278,63 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
798
1278
  }
799
1279
 
800
1280
  /**
801
- * Handle cross-route completion evaluation and notifications
802
- * This method evaluates all routes for completion and can trigger completion handlers
1281
+ * Handle cross-flow completion evaluation and notifications
1282
+ * This method evaluates all flows for completion and can trigger completion handlers
803
1283
  */
804
- async handleCrossRouteCompletion(params: {
805
- routes: Route<TContext, TData>[];
1284
+ async handleCrossFlowCompletion(params: {
1285
+ flows: Flow<TContext, TData>[];
806
1286
  session: SessionState<TData>;
807
1287
  context: TContext;
808
1288
  history: Event[];
809
1289
  }): Promise<{
810
1290
  session: SessionState<TData>;
811
- completedRoutes: Route<TContext, TData>[];
1291
+ completedFlows: Flow<TContext, TData>[];
812
1292
  pendingTransitions: Array<{
813
- route: Route<TContext, TData>;
814
- transitionConfig: RouteTransitionConfig<TContext, TData>;
1293
+ flow: Flow<TContext, TData>;
1294
+ transitionConfig: Directive<TContext, TData>;
815
1295
  }>;
816
1296
  }> {
817
- const { routes, session, context } = params;
1297
+ const { flows, session, context } = params;
818
1298
 
819
- // Evaluate all routes for completion
820
- const completedRoutes: Route<TContext, TData>[] = [];
1299
+ // Evaluate all flows for completion
1300
+ const completedFlows: Flow<TContext, TData>[] = [];
821
1301
  const pendingTransitions: Array<{
822
- route: Route<TContext, TData>;
823
- transitionConfig: RouteTransitionConfig<TContext, TData>;
1302
+ flow: Flow<TContext, TData>;
1303
+ transitionConfig: Directive<TContext, TData>;
824
1304
  }> = [];
825
1305
 
826
- for (const route of routes) {
827
- if (route.isComplete(session.data || {})) {
828
- completedRoutes.push(route);
1306
+ for (const flow of flows) {
1307
+ if (flow.isComplete(session.data || {})) {
1308
+ completedFlows.push(flow);
829
1309
 
830
1310
  // Check for onComplete transitions
831
- const transitionConfig = await route.evaluateOnComplete(
1311
+ const transitionConfig = await flow.evaluateOnComplete(
832
1312
  { data: session.data },
833
1313
  context
834
1314
  );
835
1315
 
836
1316
  if (transitionConfig) {
837
- pendingTransitions.push({ route, transitionConfig });
1317
+ pendingTransitions.push({ flow, transitionConfig });
838
1318
  }
839
1319
 
840
1320
  logger.debug(
841
- `[ResponsePipeline] Route completed: ${route.title} ` +
842
- `(${Math.round(route.getCompletionProgress(session.data || {}) * 100)}%)`
1321
+ `[ResponsePipeline] Flow completed: ${flow.title} ` +
1322
+ `(${Math.round(flow.getCompletionProgress(session.data || {}) * 100)}%)`
843
1323
  );
844
1324
  }
845
1325
  }
846
1326
 
847
- // Log completion status for all routes
848
- if (completedRoutes.length > 0) {
1327
+ // Log completion status for all flows
1328
+ if (completedFlows.length > 0) {
849
1329
  logger.debug(
850
- `[ResponsePipeline] Cross-route completion evaluation: ` +
851
- `${completedRoutes.length}/${routes.length} routes complete`
1330
+ `[ResponsePipeline] Cross-flow completion evaluation: ` +
1331
+ `${completedFlows.length}/${flows.length} flows complete`
852
1332
  );
853
1333
  }
854
1334
 
855
1335
  return {
856
1336
  session,
857
- completedRoutes,
1337
+ completedFlows,
858
1338
  pendingTransitions,
859
1339
  };
860
1340
  }
@@ -866,9 +1346,9 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
866
1346
  async updateDataFlow(params: {
867
1347
  session: SessionState<TData>;
868
1348
  dataUpdate: Partial<TData>;
869
- routes: Route<TContext, TData>[];
1349
+ flows: Flow<TContext, TData>[];
870
1350
  }): Promise<SessionState<TData>> {
871
- const { session, dataUpdate, routes } = params;
1351
+ const { session, dataUpdate, flows } = params;
872
1352
 
873
1353
  // Update session data
874
1354
  const updatedSession = await this.updateData(session, dataUpdate);
@@ -878,18 +1358,18 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
878
1358
  await this.updateCollectedData(dataUpdate);
879
1359
  }
880
1360
 
881
- // Evaluate route completions after data update
882
- const completionResults = await this.handleCrossRouteCompletion({
883
- routes,
1361
+ // Evaluate flow completions after data update
1362
+ const completionResults = await this.handleCrossFlowCompletion({
1363
+ flows,
884
1364
  session: updatedSession,
885
1365
  context: this.context!,
886
1366
  history: [],
887
1367
  });
888
1368
 
889
- // Log any newly completed routes
890
- if (completionResults.completedRoutes.length > 0) {
1369
+ // Log any newly completed flows
1370
+ if (completionResults.completedFlows.length > 0) {
891
1371
  logger.debug(
892
- `[ResponsePipeline] Data update resulted in ${completionResults.completedRoutes.length} completed routes`
1372
+ `[ResponsePipeline] Data update resulted in ${completionResults.completedFlows.length} completed flows`
893
1373
  );
894
1374
  }
895
1375