@falai/agent 1.2.7 → 2.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 (508) 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 +1196 -1015
  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 +524 -134
  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/GeminiProvider.d.ts +3 -3
  126. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  127. package/dist/cjs/providers/GeminiProvider.js +16 -14
  128. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  129. package/dist/cjs/types/agent.d.ts +183 -54
  130. package/dist/cjs/types/agent.d.ts.map +1 -1
  131. package/dist/cjs/types/agent.js +0 -6
  132. package/dist/cjs/types/agent.js.map +1 -1
  133. package/dist/cjs/types/ai.d.ts +3 -3
  134. package/dist/cjs/types/ai.d.ts.map +1 -1
  135. package/dist/cjs/types/errors.d.ts +15 -0
  136. package/dist/cjs/types/errors.d.ts.map +1 -0
  137. package/dist/cjs/types/errors.js +22 -0
  138. package/dist/cjs/types/errors.js.map +1 -0
  139. package/dist/cjs/types/flow.d.ts +513 -0
  140. package/dist/cjs/types/flow.d.ts.map +1 -0
  141. package/dist/cjs/types/{route.js → flow.js} +2 -2
  142. package/dist/cjs/types/flow.js.map +1 -0
  143. package/dist/cjs/types/index.d.ts +7 -6
  144. package/dist/cjs/types/index.d.ts.map +1 -1
  145. package/dist/cjs/types/index.js +6 -2
  146. package/dist/cjs/types/index.js.map +1 -1
  147. package/dist/cjs/types/persistence.d.ts +11 -7
  148. package/dist/cjs/types/persistence.d.ts.map +1 -1
  149. package/dist/cjs/types/routing.d.ts +1 -1
  150. package/dist/cjs/types/routing.d.ts.map +1 -1
  151. package/dist/cjs/types/session.d.ts +24 -23
  152. package/dist/cjs/types/session.d.ts.map +1 -1
  153. package/dist/cjs/types/signals.d.ts +248 -0
  154. package/dist/cjs/types/signals.d.ts.map +1 -0
  155. package/dist/cjs/types/signals.js +11 -0
  156. package/dist/cjs/types/signals.js.map +1 -0
  157. package/dist/cjs/types/template.d.ts +2 -8
  158. package/dist/cjs/types/template.d.ts.map +1 -1
  159. package/dist/cjs/types/tool.d.ts +36 -29
  160. package/dist/cjs/types/tool.d.ts.map +1 -1
  161. package/dist/cjs/types/tool.js +1 -1
  162. package/dist/cjs/types/tool.js.map +1 -1
  163. package/dist/cjs/utils/condition.d.ts +7 -1
  164. package/dist/cjs/utils/condition.d.ts.map +1 -1
  165. package/dist/cjs/utils/condition.js.map +1 -1
  166. package/dist/cjs/utils/id.d.ts +13 -5
  167. package/dist/cjs/utils/id.d.ts.map +1 -1
  168. package/dist/cjs/utils/id.js +24 -10
  169. package/dist/cjs/utils/id.js.map +1 -1
  170. package/dist/cjs/utils/index.d.ts +2 -2
  171. package/dist/cjs/utils/index.d.ts.map +1 -1
  172. package/dist/cjs/utils/index.js +7 -3
  173. package/dist/cjs/utils/index.js.map +1 -1
  174. package/dist/cjs/utils/session.d.ts +44 -5
  175. package/dist/cjs/utils/session.d.ts.map +1 -1
  176. package/dist/cjs/utils/session.js +197 -38
  177. package/dist/cjs/utils/session.js.map +1 -1
  178. package/dist/constants/index.d.ts +0 -9
  179. package/dist/constants/index.d.ts.map +1 -1
  180. package/dist/constants/index.js +3 -9
  181. package/dist/constants/index.js.map +1 -1
  182. package/dist/core/Agent.d.ts +119 -153
  183. package/dist/core/Agent.d.ts.map +1 -1
  184. package/dist/core/Agent.js +472 -325
  185. package/dist/core/Agent.js.map +1 -1
  186. package/dist/core/AutoChainExecutor.d.ts +107 -0
  187. package/dist/core/AutoChainExecutor.d.ts.map +1 -0
  188. package/dist/core/AutoChainExecutor.js +293 -0
  189. package/dist/core/AutoChainExecutor.js.map +1 -0
  190. package/dist/core/BranchEvaluator.d.ts +54 -0
  191. package/dist/core/BranchEvaluator.d.ts.map +1 -0
  192. package/dist/core/BranchEvaluator.js +126 -0
  193. package/dist/core/BranchEvaluator.js.map +1 -0
  194. package/dist/core/DirectiveBus.d.ts +88 -0
  195. package/dist/core/DirectiveBus.d.ts.map +1 -0
  196. package/dist/core/DirectiveBus.js +192 -0
  197. package/dist/core/DirectiveBus.js.map +1 -0
  198. package/dist/core/DirectiveChainTracker.d.ts +49 -0
  199. package/dist/core/DirectiveChainTracker.d.ts.map +1 -0
  200. package/dist/core/DirectiveChainTracker.js +117 -0
  201. package/dist/core/DirectiveChainTracker.js.map +1 -0
  202. package/dist/core/Flow.d.ts +186 -0
  203. package/dist/core/Flow.d.ts.map +1 -0
  204. package/dist/core/Flow.js +546 -0
  205. package/dist/core/Flow.js.map +1 -0
  206. package/dist/core/FlowRouter.d.ts +182 -0
  207. package/dist/core/FlowRouter.d.ts.map +1 -0
  208. package/dist/core/{RoutingEngine.js → FlowRouter.js} +322 -305
  209. package/dist/core/FlowRouter.js.map +1 -0
  210. package/dist/core/PersistenceManager.d.ts +2 -2
  211. package/dist/core/PersistenceManager.d.ts.map +1 -1
  212. package/dist/core/PersistenceManager.js +7 -7
  213. package/dist/core/PersistenceManager.js.map +1 -1
  214. package/dist/core/PromptComposer.d.ts +21 -8
  215. package/dist/core/PromptComposer.d.ts.map +1 -1
  216. package/dist/core/PromptComposer.js +183 -106
  217. package/dist/core/PromptComposer.js.map +1 -1
  218. package/dist/core/PromptSectionCache.d.ts +1 -1
  219. package/dist/core/PromptSectionCache.js +1 -1
  220. package/dist/core/ResponseEngine.d.ts +18 -8
  221. package/dist/core/ResponseEngine.d.ts.map +1 -1
  222. package/dist/core/ResponseEngine.js +38 -36
  223. package/dist/core/ResponseEngine.js.map +1 -1
  224. package/dist/core/ResponseModal.d.ts +73 -56
  225. package/dist/core/ResponseModal.d.ts.map +1 -1
  226. package/dist/core/ResponseModal.js +1198 -1017
  227. package/dist/core/ResponseModal.js.map +1 -1
  228. package/dist/core/ResponsePipeline.d.ts +124 -26
  229. package/dist/core/ResponsePipeline.d.ts.map +1 -1
  230. package/dist/core/ResponsePipeline.js +524 -135
  231. package/dist/core/ResponsePipeline.js.map +1 -1
  232. package/dist/core/SignalEvaluator.d.ts +86 -0
  233. package/dist/core/SignalEvaluator.d.ts.map +1 -0
  234. package/dist/core/SignalEvaluator.js +326 -0
  235. package/dist/core/SignalEvaluator.js.map +1 -0
  236. package/dist/core/SignalProcessor.d.ts +152 -0
  237. package/dist/core/SignalProcessor.d.ts.map +1 -0
  238. package/dist/core/SignalProcessor.js +555 -0
  239. package/dist/core/SignalProcessor.js.map +1 -0
  240. package/dist/core/Step.d.ts +43 -32
  241. package/dist/core/Step.d.ts.map +1 -1
  242. package/dist/core/Step.js +220 -126
  243. package/dist/core/Step.js.map +1 -1
  244. package/dist/core/StreamingToolExecutor.d.ts +2 -2
  245. package/dist/core/StreamingToolExecutor.d.ts.map +1 -1
  246. package/dist/core/StreamingToolExecutor.js.map +1 -1
  247. package/dist/core/ToolManager.d.ts +44 -13
  248. package/dist/core/ToolManager.d.ts.map +1 -1
  249. package/dist/core/ToolManager.js +174 -91
  250. package/dist/core/ToolManager.js.map +1 -1
  251. package/dist/core/createAgent.d.ts +35 -0
  252. package/dist/core/createAgent.d.ts.map +1 -0
  253. package/dist/core/createAgent.js +36 -0
  254. package/dist/core/createAgent.js.map +1 -0
  255. package/dist/core/flow-namespace.d.ts +49 -0
  256. package/dist/core/flow-namespace.d.ts.map +1 -0
  257. package/dist/core/flow-namespace.js +168 -0
  258. package/dist/core/flow-namespace.js.map +1 -0
  259. package/dist/index.d.ts +11 -14
  260. package/dist/index.d.ts.map +1 -1
  261. package/dist/index.js +9 -12
  262. package/dist/index.js.map +1 -1
  263. package/dist/providers/GeminiProvider.d.ts +3 -3
  264. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  265. package/dist/providers/GeminiProvider.js +16 -14
  266. package/dist/providers/GeminiProvider.js.map +1 -1
  267. package/dist/types/agent.d.ts +183 -54
  268. package/dist/types/agent.d.ts.map +1 -1
  269. package/dist/types/agent.js +0 -6
  270. package/dist/types/agent.js.map +1 -1
  271. package/dist/types/ai.d.ts +3 -3
  272. package/dist/types/ai.d.ts.map +1 -1
  273. package/dist/types/errors.d.ts +15 -0
  274. package/dist/types/errors.d.ts.map +1 -0
  275. package/dist/types/errors.js +18 -0
  276. package/dist/types/errors.js.map +1 -0
  277. package/dist/types/flow.d.ts +513 -0
  278. package/dist/types/flow.d.ts.map +1 -0
  279. package/dist/types/flow.js +5 -0
  280. package/dist/types/flow.js.map +1 -0
  281. package/dist/types/index.d.ts +7 -6
  282. package/dist/types/index.d.ts.map +1 -1
  283. package/dist/types/index.js +4 -1
  284. package/dist/types/index.js.map +1 -1
  285. package/dist/types/persistence.d.ts +11 -7
  286. package/dist/types/persistence.d.ts.map +1 -1
  287. package/dist/types/routing.d.ts +1 -1
  288. package/dist/types/routing.d.ts.map +1 -1
  289. package/dist/types/session.d.ts +24 -23
  290. package/dist/types/session.d.ts.map +1 -1
  291. package/dist/types/signals.d.ts +248 -0
  292. package/dist/types/signals.d.ts.map +1 -0
  293. package/dist/types/signals.js +10 -0
  294. package/dist/types/signals.js.map +1 -0
  295. package/dist/types/template.d.ts +2 -8
  296. package/dist/types/template.d.ts.map +1 -1
  297. package/dist/types/tool.d.ts +36 -29
  298. package/dist/types/tool.d.ts.map +1 -1
  299. package/dist/types/tool.js +1 -1
  300. package/dist/types/tool.js.map +1 -1
  301. package/dist/utils/condition.d.ts +7 -1
  302. package/dist/utils/condition.d.ts.map +1 -1
  303. package/dist/utils/condition.js.map +1 -1
  304. package/dist/utils/id.d.ts +13 -5
  305. package/dist/utils/id.d.ts.map +1 -1
  306. package/dist/utils/id.js +22 -9
  307. package/dist/utils/id.js.map +1 -1
  308. package/dist/utils/index.d.ts +2 -2
  309. package/dist/utils/index.d.ts.map +1 -1
  310. package/dist/utils/index.js +2 -2
  311. package/dist/utils/index.js.map +1 -1
  312. package/dist/utils/session.d.ts +44 -5
  313. package/dist/utils/session.d.ts.map +1 -1
  314. package/dist/utils/session.js +193 -37
  315. package/dist/utils/session.js.map +1 -1
  316. package/docs/README.md +15 -202
  317. package/docs/concepts/architecture.md +281 -0
  318. package/docs/concepts/directives.md +400 -0
  319. package/docs/concepts/pipeline.md +399 -0
  320. package/docs/guides/branching.md +263 -0
  321. package/docs/guides/compaction.md +163 -0
  322. package/docs/guides/conditions.md +167 -0
  323. package/docs/guides/error-handling.md +176 -0
  324. package/docs/guides/flow-control.md +409 -0
  325. package/docs/guides/instructions.md +210 -0
  326. package/docs/guides/persistence.md +182 -0
  327. package/docs/guides/streaming.md +137 -0
  328. package/docs/migration/README.md +15 -0
  329. package/docs/migration/route-to-flow.md +560 -0
  330. package/docs/migration/v1-to-v2.md +909 -0
  331. package/docs/reference/adapters.md +481 -0
  332. package/docs/reference/branches.md +241 -0
  333. package/docs/reference/create-agent.md +186 -0
  334. package/docs/reference/directive.md +243 -0
  335. package/docs/reference/errors.md +122 -0
  336. package/docs/reference/flow.md +238 -0
  337. package/docs/reference/instruction.md +177 -0
  338. package/docs/reference/pre-directive.md +131 -0
  339. package/docs/reference/providers.md +227 -0
  340. package/docs/reference/signals.md +356 -0
  341. package/docs/reference/step.md +339 -0
  342. package/docs/reference/tool.md +269 -0
  343. package/docs/start/01-install.md +81 -0
  344. package/docs/start/02-first-agent.md +196 -0
  345. package/docs/start/03-collect-data.md +222 -0
  346. package/docs/start/04-add-tools.md +276 -0
  347. package/docs/start/05-go-to-production.md +216 -0
  348. package/examples/01-quickstart.ts +20 -0
  349. package/examples/02-data-extraction.ts +90 -0
  350. package/examples/03-tools.ts +136 -0
  351. package/examples/04-instructions.ts +100 -0
  352. package/examples/05-branching.ts +140 -0
  353. package/examples/06-flow-control.ts +103 -0
  354. package/examples/07-streaming.ts +69 -0
  355. package/examples/08-persistence.ts +98 -0
  356. package/examples/09-signals.ts +144 -0
  357. package/examples/tsconfig.json +30 -0
  358. package/package.json +2 -1
  359. package/src/adapters/MemoryAdapter.ts +3 -3
  360. package/src/adapters/MongoAdapter.ts +3 -3
  361. package/src/adapters/OpenSearchAdapter.ts +10 -8
  362. package/src/adapters/PostgreSQLAdapter.ts +26 -10
  363. package/src/adapters/PrismaAdapter.ts +6 -6
  364. package/src/adapters/RedisAdapter.ts +3 -3
  365. package/src/adapters/SQLiteAdapter.ts +31 -12
  366. package/src/constants/index.ts +2 -10
  367. package/src/core/Agent.ts +585 -374
  368. package/src/core/AutoChainExecutor.ts +440 -0
  369. package/src/core/BranchEvaluator.ts +167 -0
  370. package/src/core/DirectiveBus.ts +248 -0
  371. package/src/core/DirectiveChainTracker.ts +144 -0
  372. package/src/core/Flow.ts +666 -0
  373. package/src/core/{RoutingEngine.ts → FlowRouter.ts} +385 -365
  374. package/src/core/PersistenceManager.ts +8 -8
  375. package/src/core/PromptComposer.ts +209 -140
  376. package/src/core/PromptSectionCache.ts +1 -1
  377. package/src/core/ResponseEngine.ts +61 -46
  378. package/src/core/ResponseModal.ts +1458 -1241
  379. package/src/core/ResponsePipeline.ts +675 -173
  380. package/src/core/SignalEvaluator.ts +420 -0
  381. package/src/core/SignalProcessor.ts +723 -0
  382. package/src/core/Step.ts +279 -176
  383. package/src/core/StreamingToolExecutor.ts +4 -4
  384. package/src/core/ToolManager.ts +200 -97
  385. package/src/core/createAgent.ts +40 -0
  386. package/src/core/flow-namespace.ts +219 -0
  387. package/src/index.ts +42 -36
  388. package/src/providers/GeminiProvider.ts +17 -15
  389. package/src/types/agent.ts +182 -53
  390. package/src/types/ai.ts +3 -3
  391. package/src/types/errors.ts +18 -0
  392. package/src/types/flow.ts +590 -0
  393. package/src/types/index.ts +43 -16
  394. package/src/types/persistence.ts +12 -8
  395. package/src/types/routing.ts +1 -1
  396. package/src/types/session.ts +26 -23
  397. package/src/types/signals.ts +321 -0
  398. package/src/types/template.ts +3 -11
  399. package/src/types/tool.ts +50 -42
  400. package/src/utils/condition.ts +13 -4
  401. package/src/utils/id.ts +27 -9
  402. package/src/utils/index.ts +6 -2
  403. package/src/utils/session.ts +238 -42
  404. package/dist/cjs/core/BatchExecutor.d.ts +0 -359
  405. package/dist/cjs/core/BatchExecutor.d.ts.map +0 -1
  406. package/dist/cjs/core/BatchExecutor.js +0 -861
  407. package/dist/cjs/core/BatchExecutor.js.map +0 -1
  408. package/dist/cjs/core/BatchPromptBuilder.d.ts +0 -89
  409. package/dist/cjs/core/BatchPromptBuilder.d.ts.map +0 -1
  410. package/dist/cjs/core/BatchPromptBuilder.js +0 -223
  411. package/dist/cjs/core/BatchPromptBuilder.js.map +0 -1
  412. package/dist/cjs/core/Route.d.ts +0 -180
  413. package/dist/cjs/core/Route.d.ts.map +0 -1
  414. package/dist/cjs/core/Route.js +0 -542
  415. package/dist/cjs/core/Route.js.map +0 -1
  416. package/dist/cjs/core/RoutingEngine.d.ts +0 -185
  417. package/dist/cjs/core/RoutingEngine.d.ts.map +0 -1
  418. package/dist/cjs/core/RoutingEngine.js.map +0 -1
  419. package/dist/cjs/types/route.d.ts +0 -336
  420. package/dist/cjs/types/route.d.ts.map +0 -1
  421. package/dist/cjs/types/route.js.map +0 -1
  422. package/dist/core/BatchExecutor.d.ts +0 -359
  423. package/dist/core/BatchExecutor.d.ts.map +0 -1
  424. package/dist/core/BatchExecutor.js +0 -856
  425. package/dist/core/BatchExecutor.js.map +0 -1
  426. package/dist/core/BatchPromptBuilder.d.ts +0 -89
  427. package/dist/core/BatchPromptBuilder.d.ts.map +0 -1
  428. package/dist/core/BatchPromptBuilder.js +0 -219
  429. package/dist/core/BatchPromptBuilder.js.map +0 -1
  430. package/dist/core/Route.d.ts +0 -180
  431. package/dist/core/Route.d.ts.map +0 -1
  432. package/dist/core/Route.js +0 -538
  433. package/dist/core/Route.js.map +0 -1
  434. package/dist/core/RoutingEngine.d.ts +0 -185
  435. package/dist/core/RoutingEngine.d.ts.map +0 -1
  436. package/dist/core/RoutingEngine.js.map +0 -1
  437. package/dist/types/route.d.ts +0 -336
  438. package/dist/types/route.d.ts.map +0 -1
  439. package/dist/types/route.js +0 -5
  440. package/dist/types/route.js.map +0 -1
  441. package/docs/CONTRIBUTING.md +0 -521
  442. package/docs/api/README.md +0 -3299
  443. package/docs/api/overview.md +0 -1410
  444. package/docs/architecture/data-extraction-flow.md +0 -360
  445. package/docs/architecture/multi-step-execution.md +0 -277
  446. package/docs/core/agent/README.md +0 -938
  447. package/docs/core/agent/context-management.md +0 -796
  448. package/docs/core/agent/rules-and-prohibitions.md +0 -113
  449. package/docs/core/agent/session-management.md +0 -693
  450. package/docs/core/ai-integration/prompt-composition.md +0 -355
  451. package/docs/core/ai-integration/providers.md +0 -515
  452. package/docs/core/ai-integration/response-processing.md +0 -433
  453. package/docs/core/conversation-flows/data-collection.md +0 -772
  454. package/docs/core/conversation-flows/route-dsl.md +0 -509
  455. package/docs/core/conversation-flows/routes.md +0 -249
  456. package/docs/core/conversation-flows/step-transitions.md +0 -731
  457. package/docs/core/conversation-flows/steps.md +0 -268
  458. package/docs/core/error-handling.md +0 -830
  459. package/docs/core/persistence/adapters.md +0 -255
  460. package/docs/core/persistence/session-storage.md +0 -656
  461. package/docs/core/routing/intelligent-routing.md +0 -470
  462. package/docs/core/tools/enhanced-tool.md +0 -186
  463. package/docs/core/tools/streaming-execution.md +0 -161
  464. package/docs/core/tools/tool-definition.md +0 -970
  465. package/docs/core/tools/tool-scoping.md +0 -819
  466. package/docs/guides/advanced-patterns/publishing.md +0 -186
  467. package/docs/guides/context-compaction.md +0 -96
  468. package/docs/guides/error-handling-patterns.md +0 -578
  469. package/docs/guides/getting-started/README.md +0 -795
  470. package/docs/guides/migration/README.md +0 -101
  471. package/docs/guides/migration/flexible-routing-conditions.md +0 -375
  472. package/docs/guides/migration/multi-step-execution.md +0 -393
  473. package/docs/guides/migration/response-modal-refactor.md +0 -518
  474. package/docs/guides/prompt-optimization.md +0 -164
  475. package/examples/advanced-patterns/context-compaction.ts +0 -223
  476. package/examples/advanced-patterns/knowledge-based-agent.ts +0 -735
  477. package/examples/advanced-patterns/persistent-onboarding.ts +0 -728
  478. package/examples/advanced-patterns/route-lifecycle-hooks.ts +0 -556
  479. package/examples/advanced-patterns/streaming-responses.ts +0 -656
  480. package/examples/ai-providers/anthropic-integration.ts +0 -388
  481. package/examples/ai-providers/openai-integration.ts +0 -228
  482. package/examples/condition-patterns/function-only-conditions.ts +0 -365
  483. package/examples/condition-patterns/mixed-array-conditions.ts +0 -477
  484. package/examples/condition-patterns/route-skipif-patterns.ts +0 -468
  485. package/examples/condition-patterns/step-skipif-patterns.ts +0 -0
  486. package/examples/condition-patterns/string-only-conditions.ts +0 -296
  487. package/examples/conversation-flows/completion-transitions.ts +0 -318
  488. package/examples/core-concepts/basic-agent.ts +0 -503
  489. package/examples/core-concepts/modern-streaming-api.ts +0 -309
  490. package/examples/core-concepts/schema-driven-extraction.ts +0 -332
  491. package/examples/core-concepts/session-management.ts +0 -494
  492. package/examples/integrations/database-integration.ts +0 -631
  493. package/examples/integrations/healthcare-integration.ts +0 -595
  494. package/examples/integrations/search-integration.ts +0 -530
  495. package/examples/integrations/server-session-management.ts +0 -307
  496. package/examples/persistence/custom-adapter.ts +0 -526
  497. package/examples/persistence/database-persistence.ts +0 -583
  498. package/examples/persistence/memory-sessions.ts +0 -495
  499. package/examples/persistence/prisma-schema.example.prisma +0 -74
  500. package/examples/persistence/redis-persistence.ts +0 -488
  501. package/examples/tools/basic-tools.ts +0 -765
  502. package/examples/tools/data-enrichment-tools.ts +0 -593
  503. package/examples/tools/enhanced-tool-metadata.ts +0 -268
  504. package/examples/tools/streaming-tool-execution.ts +0 -283
  505. package/src/core/BatchExecutor.ts +0 -1187
  506. package/src/core/BatchPromptBuilder.ts +0 -299
  507. package/src/core/Route.ts +0 -678
  508. 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
+ };
296
+
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
+ );
157
307
 
158
- // Merge initial data if available
159
- if (targetRoute.initialData) {
160
- targetSession = mergeCollected(
161
- targetSession,
162
- targetRoute.initialData
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
+ }
164
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
+ }
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
+ }
226
471
 
227
- if (!selectedRoute || isRouteComplete) {
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
+ }
503
+
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,17 +528,39 @@ 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
  }
263
541
 
542
+ // Before entering the step, check if requires fields are satisfied
543
+ if (nextStep.requires && nextStep.requires.length > 0) {
544
+ const sessionData = session.data || {};
545
+ const missingRequires = nextStep.requires.filter(
546
+ field => (sessionData as Record<string, unknown>)[String(field)] === undefined
547
+ );
548
+ if (missingRequires.length > 0) {
549
+ logger.debug(
550
+ `[ResponseHandler] Cannot enter step "${nextStep.id}": missing required fields [${missingRequires.join(', ')}]. Staying at current step.`
551
+ );
552
+ // Stay at current step - don't enter the next one
553
+ const currentStepId = session.currentStep?.id;
554
+ if (currentStepId && selectedFlow) {
555
+ const currentStepInstance = selectedFlow.getStep(currentStepId);
556
+ if (currentStepInstance) {
557
+ nextStep = currentStepInstance;
558
+ }
559
+ }
560
+ return { nextStep, session };
561
+ }
562
+ }
563
+
264
564
  // Update session with next step
265
565
  const updatedSession = enterStep(
266
566
  session,
@@ -272,12 +572,207 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
272
572
  return { nextStep, session: updatedSession };
273
573
  }
274
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
+
275
770
  /**
276
771
  * Execute tool calls and handle results
277
772
  */
278
773
  async executeToolCalls(params: {
279
774
  toolCalls: Array<{ toolName: string; arguments: Record<string, unknown> }>;
280
- selectedRoute?: Route<TContext, TData>;
775
+ selectedFlow?: Flow<TContext, TData>;
281
776
  context: TContext;
282
777
  session: SessionState<TData>;
283
778
  history: Event[];
@@ -285,7 +780,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
285
780
  }): Promise<ToolExecutionResult<TData>> {
286
781
  const {
287
782
  toolCalls,
288
- selectedRoute,
783
+ selectedFlow,
289
784
  context,
290
785
  session,
291
786
  history,
@@ -309,9 +804,9 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
309
804
  const toolResults = new Map<string, string>();
310
805
 
311
806
  for (const toolCall of toolCalls) {
312
- const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
807
+ const tool = this.findAvailableTool(toolCall.toolName, selectedFlow);
313
808
  if (!tool) {
314
- 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.`);
315
810
  continue;
316
811
  }
317
812
 
@@ -355,7 +850,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
355
850
  }
356
851
 
357
852
  logger.debug(
358
- `[ResponseHandler] Executed ${isStreaming ? "streaming " : ""}tool: ${String(tool.id || tool.name || 'unknown')
853
+ `[ResponseHandler] Executed ${isStreaming ? "streaming " : ""}tool: ${String(tool.id || tool.id || 'unknown')
359
854
  } (success: ${result.success})`
360
855
  );
361
856
  }
@@ -374,7 +869,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
374
869
  initialToolCalls:
375
870
  | Array<{ toolName: string; arguments: Record<string, unknown> }>
376
871
  | undefined;
377
- selectedRoute?: Route<TContext, TData>;
872
+ selectedFlow?: Flow<TContext, TData>;
378
873
  nextStep: Step<TContext, TData>;
379
874
  responsePrompt: string;
380
875
  history: HistoryItem[];
@@ -385,7 +880,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
385
880
  }): Promise<ToolExecutionResult<TData>> {
386
881
  const {
387
882
  initialToolCalls,
388
- selectedRoute,
883
+ selectedFlow,
389
884
  nextStep,
390
885
  responsePrompt,
391
886
  history,
@@ -413,7 +908,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
413
908
  // Add tool execution results to history so AI knows what happened
414
909
  const toolResultItems: HistoryItem[] = [];
415
910
  for (const toolCall of currentToolCalls || []) {
416
- const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
911
+ const tool = this.findAvailableTool(toolCall.toolName, selectedFlow);
417
912
  if (tool) {
418
913
  toolResultItems.push({
419
914
  role: "assistant" as const,
@@ -441,7 +936,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
441
936
  prompt: responsePrompt,
442
937
  history: updatedHistory,
443
938
  context,
444
- tools: this.collectAvailableTools(selectedRoute, nextStep),
939
+ tools: this.collectAvailableTools(selectedFlow, nextStep),
445
940
  parameters: {
446
941
  jsonSchema: responseSchema,
447
942
  schemaName: isStreaming ? "tool_followup_streaming" : "tool_followup",
@@ -461,7 +956,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
461
956
  // Execute the follow-up tool calls
462
957
  const toolResult = await this.executeToolCalls({
463
958
  toolCalls: followUpToolCalls!,
464
- selectedRoute,
959
+ selectedFlow,
465
960
  context,
466
961
  session: currentSession,
467
962
  history: historyToEvents(updatedHistory),
@@ -484,8 +979,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
484
979
 
485
980
  if (toolLoopCount >= MAX_TOOL_LOOPS) {
486
981
  logger.warn(
487
- `[ResponseHandler] ${isStreaming ? "Streaming " : ""
488
- }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.`
489
983
  );
490
984
  }
491
985
 
@@ -558,66 +1052,74 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
558
1052
  }
559
1053
 
560
1054
  /**
561
- * 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.
562
1062
  */
563
- async handleRouteCompletion(params: {
564
- selectedRoute: Route<TContext, TData>;
1063
+ async handleFlowCompletion(params: {
1064
+ selectedFlow: Flow<TContext, TData>;
565
1065
  session: SessionState<TData>;
566
1066
  context: TContext;
567
1067
  history: Event[];
568
1068
  }): Promise<{ session: SessionState<TData>; hasTransition: boolean }> {
569
- const { selectedRoute, session, context, history } = params;
1069
+ const { selectedFlow, session, context } = params;
570
1070
 
571
1071
  // Check for onComplete transition
572
- const transitionConfig = await selectedRoute.evaluateOnComplete(
1072
+ const transitionConfig = await selectedFlow.evaluateOnComplete(
573
1073
  { data: session.data },
574
1074
  context
575
1075
  );
576
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
+
577
1089
  if (transitionConfig) {
578
- // Find target route by ID or title
579
- const targetRoute = this.getRoutes().find(
580
- (r) =>
581
- r.id === transitionConfig.nextStep ||
582
- r.title === transitionConfig.nextStep
583
- );
1090
+ // Extract target from directive's goTo field
1091
+ const goToTarget = typeof transitionConfig.goTo === 'string'
1092
+ ? transitionConfig.goTo
1093
+ : transitionConfig.goTo?.flow;
584
1094
 
585
- if (targetRoute) {
586
- const templateContext = createTemplateContext({
587
- context,
588
- session,
589
- history,
590
- });
591
- const renderedCondition: string =
592
- (await render(transitionConfig.condition, templateContext)) ||
593
- (typeof transitionConfig.condition === "string"
594
- ? transitionConfig.condition
595
- : "");
596
-
597
- // Set pending transition in session
598
- const updatedSession = {
599
- ...session,
600
- pendingTransition: {
601
- targetRouteId: targetRoute.id,
602
- condition: renderedCondition,
603
- 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,
604
1107
  },
605
1108
  };
606
1109
  logger.debug(
607
- `[ResponseHandler] Route ${selectedRoute.title} completed with pending transition to: ${targetRoute.title}`
1110
+ `[ResponseHandler] Flow ${selectedFlow.title} completed with pending directive to: ${targetFlow.title}`
608
1111
  );
609
1112
  return { session: updatedSession, hasTransition: true };
610
- } else {
1113
+ } else if (goToTarget) {
611
1114
  logger.warn(
612
- `[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.`
613
1117
  );
614
1118
  }
615
1119
  }
616
1120
 
617
- // Set step to END_ROUTE marker
618
- const updatedSession = enterStep(session, END_ROUTE_ID, "Route completed");
619
1121
  logger.debug(
620
- `[ResponseHandler] Route ${selectedRoute.title} completed. Entered END_ROUTE step.`
1122
+ `[ResponseHandler] Flow ${selectedFlow.title} completed; session released to idle.`
621
1123
  );
622
1124
 
623
1125
  return { session: updatedSession, hasTransition: false };
@@ -625,46 +1127,46 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
625
1127
 
626
1128
 
627
1129
  /**
628
- * 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
629
1131
  * Delegates to ToolManager for unified tool resolution
630
1132
  */
631
1133
  private findAvailableTool(
632
1134
  toolName: string,
633
- route?: Route<TContext, TData>
1135
+ flow?: Flow<TContext, TData>
634
1136
  ): Tool<TContext, TData> | undefined {
635
1137
  // Use ToolManager for unified tool resolution if available
636
1138
  if (this.toolManager) {
637
- return this.toolManager.find(toolName, undefined, undefined, route);
1139
+ return this.toolManager.find(toolName, undefined, undefined, flow);
638
1140
  }
639
1141
 
640
1142
  // Fallback to legacy resolution if ToolManager not available
641
1143
  logger.warn(`[ResponsePipeline] ToolManager not available, using legacy tool resolution for: ${toolName}`);
642
1144
 
643
- // Check route-level tools first (if route provided)
644
- if (route) {
645
- const routeTool = route
1145
+ // Check flow-level tools first (if flow provided)
1146
+ if (flow) {
1147
+ const flowTool = flow
646
1148
  .getTools()
647
- .find((tool) => tool.id === toolName || tool.name === toolName);
648
- if (routeTool) return routeTool;
1149
+ .find((tool) => tool.id === toolName || tool.id === toolName);
1150
+ if (flowTool) return flowTool;
649
1151
  }
650
1152
 
651
1153
  // Fall back to agent-level tools
652
1154
  return this.tools.find(
653
- (tool) => tool.id === toolName || tool.name === toolName
1155
+ (tool) => tool.id === toolName || tool.id === toolName
654
1156
  );
655
1157
  }
656
1158
 
657
1159
  /**
658
- * 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
659
1161
  * Delegates to ToolManager for unified tool resolution and deduplication
660
1162
  */
661
1163
  private collectAvailableTools(
662
- route?: Route<TContext, TData>,
1164
+ flow?: Flow<TContext, TData>,
663
1165
  step?: Step<TContext, TData>
664
1166
  ): Array<{ id: string; description?: string; parameters?: unknown }> {
665
1167
  // Use ToolManager for unified tool collection if available
666
1168
  if (this.toolManager) {
667
- const availableTools = this.toolManager.getAvailable(undefined, step, route);
1169
+ const availableTools = this.toolManager.getAvailable(undefined, step, flow);
668
1170
  return availableTools.map((tool) => ({
669
1171
  id: tool.id,
670
1172
  description: tool.description,
@@ -685,9 +1187,9 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
685
1187
  availableTools.set(tool.id, tool);
686
1188
  });
687
1189
 
688
- // Add route-level tools (these take precedence)
689
- if (route) {
690
- route.getTools().forEach((tool) => {
1190
+ // Add flow-level tools (these take precedence)
1191
+ if (flow) {
1192
+ flow.getTools().forEach((tool) => {
691
1193
  availableTools.set(tool.id, tool);
692
1194
  });
693
1195
  }
@@ -776,63 +1278,63 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
776
1278
  }
777
1279
 
778
1280
  /**
779
- * Handle cross-route completion evaluation and notifications
780
- * 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
781
1283
  */
782
- async handleCrossRouteCompletion(params: {
783
- routes: Route<TContext, TData>[];
1284
+ async handleCrossFlowCompletion(params: {
1285
+ flows: Flow<TContext, TData>[];
784
1286
  session: SessionState<TData>;
785
1287
  context: TContext;
786
1288
  history: Event[];
787
1289
  }): Promise<{
788
1290
  session: SessionState<TData>;
789
- completedRoutes: Route<TContext, TData>[];
1291
+ completedFlows: Flow<TContext, TData>[];
790
1292
  pendingTransitions: Array<{
791
- route: Route<TContext, TData>;
792
- transitionConfig: RouteTransitionConfig<TContext, TData>;
1293
+ flow: Flow<TContext, TData>;
1294
+ transitionConfig: Directive<TContext, TData>;
793
1295
  }>;
794
1296
  }> {
795
- const { routes, session, context } = params;
1297
+ const { flows, session, context } = params;
796
1298
 
797
- // Evaluate all routes for completion
798
- const completedRoutes: Route<TContext, TData>[] = [];
1299
+ // Evaluate all flows for completion
1300
+ const completedFlows: Flow<TContext, TData>[] = [];
799
1301
  const pendingTransitions: Array<{
800
- route: Route<TContext, TData>;
801
- transitionConfig: RouteTransitionConfig<TContext, TData>;
1302
+ flow: Flow<TContext, TData>;
1303
+ transitionConfig: Directive<TContext, TData>;
802
1304
  }> = [];
803
1305
 
804
- for (const route of routes) {
805
- if (route.isComplete(session.data || {})) {
806
- completedRoutes.push(route);
1306
+ for (const flow of flows) {
1307
+ if (flow.isComplete(session.data || {})) {
1308
+ completedFlows.push(flow);
807
1309
 
808
1310
  // Check for onComplete transitions
809
- const transitionConfig = await route.evaluateOnComplete(
1311
+ const transitionConfig = await flow.evaluateOnComplete(
810
1312
  { data: session.data },
811
1313
  context
812
1314
  );
813
1315
 
814
1316
  if (transitionConfig) {
815
- pendingTransitions.push({ route, transitionConfig });
1317
+ pendingTransitions.push({ flow, transitionConfig });
816
1318
  }
817
1319
 
818
1320
  logger.debug(
819
- `[ResponsePipeline] Route completed: ${route.title} ` +
820
- `(${Math.round(route.getCompletionProgress(session.data || {}) * 100)}%)`
1321
+ `[ResponsePipeline] Flow completed: ${flow.title} ` +
1322
+ `(${Math.round(flow.getCompletionProgress(session.data || {}) * 100)}%)`
821
1323
  );
822
1324
  }
823
1325
  }
824
1326
 
825
- // Log completion status for all routes
826
- if (completedRoutes.length > 0) {
1327
+ // Log completion status for all flows
1328
+ if (completedFlows.length > 0) {
827
1329
  logger.debug(
828
- `[ResponsePipeline] Cross-route completion evaluation: ` +
829
- `${completedRoutes.length}/${routes.length} routes complete`
1330
+ `[ResponsePipeline] Cross-flow completion evaluation: ` +
1331
+ `${completedFlows.length}/${flows.length} flows complete`
830
1332
  );
831
1333
  }
832
1334
 
833
1335
  return {
834
1336
  session,
835
- completedRoutes,
1337
+ completedFlows,
836
1338
  pendingTransitions,
837
1339
  };
838
1340
  }
@@ -844,9 +1346,9 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
844
1346
  async updateDataFlow(params: {
845
1347
  session: SessionState<TData>;
846
1348
  dataUpdate: Partial<TData>;
847
- routes: Route<TContext, TData>[];
1349
+ flows: Flow<TContext, TData>[];
848
1350
  }): Promise<SessionState<TData>> {
849
- const { session, dataUpdate, routes } = params;
1351
+ const { session, dataUpdate, flows } = params;
850
1352
 
851
1353
  // Update session data
852
1354
  const updatedSession = await this.updateData(session, dataUpdate);
@@ -856,18 +1358,18 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
856
1358
  await this.updateCollectedData(dataUpdate);
857
1359
  }
858
1360
 
859
- // Evaluate route completions after data update
860
- const completionResults = await this.handleCrossRouteCompletion({
861
- routes,
1361
+ // Evaluate flow completions after data update
1362
+ const completionResults = await this.handleCrossFlowCompletion({
1363
+ flows,
862
1364
  session: updatedSession,
863
1365
  context: this.context!,
864
1366
  history: [],
865
1367
  });
866
1368
 
867
- // Log any newly completed routes
868
- if (completionResults.completedRoutes.length > 0) {
1369
+ // Log any newly completed flows
1370
+ if (completionResults.completedFlows.length > 0) {
869
1371
  logger.debug(
870
- `[ResponsePipeline] Data update resulted in ${completionResults.completedRoutes.length} completed routes`
1372
+ `[ResponsePipeline] Data update resulted in ${completionResults.completedFlows.length} completed flows`
871
1373
  );
872
1374
  }
873
1375