@falai/agent 1.2.8 → 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 (499) 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/types/agent.d.ts +183 -54
  126. package/dist/cjs/types/agent.d.ts.map +1 -1
  127. package/dist/cjs/types/agent.js +0 -6
  128. package/dist/cjs/types/agent.js.map +1 -1
  129. package/dist/cjs/types/ai.d.ts +3 -3
  130. package/dist/cjs/types/ai.d.ts.map +1 -1
  131. package/dist/cjs/types/errors.d.ts +15 -0
  132. package/dist/cjs/types/errors.d.ts.map +1 -0
  133. package/dist/cjs/types/errors.js +22 -0
  134. package/dist/cjs/types/errors.js.map +1 -0
  135. package/dist/cjs/types/flow.d.ts +513 -0
  136. package/dist/cjs/types/flow.d.ts.map +1 -0
  137. package/dist/cjs/types/{route.js → flow.js} +2 -2
  138. package/dist/cjs/types/flow.js.map +1 -0
  139. package/dist/cjs/types/index.d.ts +7 -6
  140. package/dist/cjs/types/index.d.ts.map +1 -1
  141. package/dist/cjs/types/index.js +6 -2
  142. package/dist/cjs/types/index.js.map +1 -1
  143. package/dist/cjs/types/persistence.d.ts +11 -7
  144. package/dist/cjs/types/persistence.d.ts.map +1 -1
  145. package/dist/cjs/types/routing.d.ts +1 -1
  146. package/dist/cjs/types/routing.d.ts.map +1 -1
  147. package/dist/cjs/types/session.d.ts +24 -23
  148. package/dist/cjs/types/session.d.ts.map +1 -1
  149. package/dist/cjs/types/signals.d.ts +248 -0
  150. package/dist/cjs/types/signals.d.ts.map +1 -0
  151. package/dist/cjs/types/signals.js +11 -0
  152. package/dist/cjs/types/signals.js.map +1 -0
  153. package/dist/cjs/types/template.d.ts +2 -8
  154. package/dist/cjs/types/template.d.ts.map +1 -1
  155. package/dist/cjs/types/tool.d.ts +36 -29
  156. package/dist/cjs/types/tool.d.ts.map +1 -1
  157. package/dist/cjs/types/tool.js +1 -1
  158. package/dist/cjs/types/tool.js.map +1 -1
  159. package/dist/cjs/utils/condition.d.ts +7 -1
  160. package/dist/cjs/utils/condition.d.ts.map +1 -1
  161. package/dist/cjs/utils/condition.js.map +1 -1
  162. package/dist/cjs/utils/id.d.ts +13 -5
  163. package/dist/cjs/utils/id.d.ts.map +1 -1
  164. package/dist/cjs/utils/id.js +24 -10
  165. package/dist/cjs/utils/id.js.map +1 -1
  166. package/dist/cjs/utils/index.d.ts +2 -2
  167. package/dist/cjs/utils/index.d.ts.map +1 -1
  168. package/dist/cjs/utils/index.js +7 -3
  169. package/dist/cjs/utils/index.js.map +1 -1
  170. package/dist/cjs/utils/session.d.ts +44 -5
  171. package/dist/cjs/utils/session.d.ts.map +1 -1
  172. package/dist/cjs/utils/session.js +197 -38
  173. package/dist/cjs/utils/session.js.map +1 -1
  174. package/dist/constants/index.d.ts +0 -9
  175. package/dist/constants/index.d.ts.map +1 -1
  176. package/dist/constants/index.js +3 -9
  177. package/dist/constants/index.js.map +1 -1
  178. package/dist/core/Agent.d.ts +119 -153
  179. package/dist/core/Agent.d.ts.map +1 -1
  180. package/dist/core/Agent.js +472 -325
  181. package/dist/core/Agent.js.map +1 -1
  182. package/dist/core/AutoChainExecutor.d.ts +107 -0
  183. package/dist/core/AutoChainExecutor.d.ts.map +1 -0
  184. package/dist/core/AutoChainExecutor.js +293 -0
  185. package/dist/core/AutoChainExecutor.js.map +1 -0
  186. package/dist/core/BranchEvaluator.d.ts +54 -0
  187. package/dist/core/BranchEvaluator.d.ts.map +1 -0
  188. package/dist/core/BranchEvaluator.js +126 -0
  189. package/dist/core/BranchEvaluator.js.map +1 -0
  190. package/dist/core/DirectiveBus.d.ts +88 -0
  191. package/dist/core/DirectiveBus.d.ts.map +1 -0
  192. package/dist/core/DirectiveBus.js +192 -0
  193. package/dist/core/DirectiveBus.js.map +1 -0
  194. package/dist/core/DirectiveChainTracker.d.ts +49 -0
  195. package/dist/core/DirectiveChainTracker.d.ts.map +1 -0
  196. package/dist/core/DirectiveChainTracker.js +117 -0
  197. package/dist/core/DirectiveChainTracker.js.map +1 -0
  198. package/dist/core/Flow.d.ts +186 -0
  199. package/dist/core/Flow.d.ts.map +1 -0
  200. package/dist/core/Flow.js +546 -0
  201. package/dist/core/Flow.js.map +1 -0
  202. package/dist/core/FlowRouter.d.ts +182 -0
  203. package/dist/core/FlowRouter.d.ts.map +1 -0
  204. package/dist/core/{RoutingEngine.js → FlowRouter.js} +322 -305
  205. package/dist/core/FlowRouter.js.map +1 -0
  206. package/dist/core/PersistenceManager.d.ts +2 -2
  207. package/dist/core/PersistenceManager.d.ts.map +1 -1
  208. package/dist/core/PersistenceManager.js +7 -7
  209. package/dist/core/PersistenceManager.js.map +1 -1
  210. package/dist/core/PromptComposer.d.ts +21 -8
  211. package/dist/core/PromptComposer.d.ts.map +1 -1
  212. package/dist/core/PromptComposer.js +183 -106
  213. package/dist/core/PromptComposer.js.map +1 -1
  214. package/dist/core/PromptSectionCache.d.ts +1 -1
  215. package/dist/core/PromptSectionCache.js +1 -1
  216. package/dist/core/ResponseEngine.d.ts +18 -8
  217. package/dist/core/ResponseEngine.d.ts.map +1 -1
  218. package/dist/core/ResponseEngine.js +38 -36
  219. package/dist/core/ResponseEngine.js.map +1 -1
  220. package/dist/core/ResponseModal.d.ts +73 -56
  221. package/dist/core/ResponseModal.d.ts.map +1 -1
  222. package/dist/core/ResponseModal.js +1193 -1016
  223. package/dist/core/ResponseModal.js.map +1 -1
  224. package/dist/core/ResponsePipeline.d.ts +124 -26
  225. package/dist/core/ResponsePipeline.d.ts.map +1 -1
  226. package/dist/core/ResponsePipeline.js +509 -137
  227. package/dist/core/ResponsePipeline.js.map +1 -1
  228. package/dist/core/SignalEvaluator.d.ts +86 -0
  229. package/dist/core/SignalEvaluator.d.ts.map +1 -0
  230. package/dist/core/SignalEvaluator.js +326 -0
  231. package/dist/core/SignalEvaluator.js.map +1 -0
  232. package/dist/core/SignalProcessor.d.ts +152 -0
  233. package/dist/core/SignalProcessor.d.ts.map +1 -0
  234. package/dist/core/SignalProcessor.js +555 -0
  235. package/dist/core/SignalProcessor.js.map +1 -0
  236. package/dist/core/Step.d.ts +43 -32
  237. package/dist/core/Step.d.ts.map +1 -1
  238. package/dist/core/Step.js +220 -126
  239. package/dist/core/Step.js.map +1 -1
  240. package/dist/core/StreamingToolExecutor.d.ts +2 -2
  241. package/dist/core/StreamingToolExecutor.d.ts.map +1 -1
  242. package/dist/core/StreamingToolExecutor.js.map +1 -1
  243. package/dist/core/ToolManager.d.ts +44 -13
  244. package/dist/core/ToolManager.d.ts.map +1 -1
  245. package/dist/core/ToolManager.js +174 -91
  246. package/dist/core/ToolManager.js.map +1 -1
  247. package/dist/core/createAgent.d.ts +35 -0
  248. package/dist/core/createAgent.d.ts.map +1 -0
  249. package/dist/core/createAgent.js +36 -0
  250. package/dist/core/createAgent.js.map +1 -0
  251. package/dist/core/flow-namespace.d.ts +49 -0
  252. package/dist/core/flow-namespace.d.ts.map +1 -0
  253. package/dist/core/flow-namespace.js +168 -0
  254. package/dist/core/flow-namespace.js.map +1 -0
  255. package/dist/index.d.ts +11 -14
  256. package/dist/index.d.ts.map +1 -1
  257. package/dist/index.js +9 -12
  258. package/dist/index.js.map +1 -1
  259. package/dist/types/agent.d.ts +183 -54
  260. package/dist/types/agent.d.ts.map +1 -1
  261. package/dist/types/agent.js +0 -6
  262. package/dist/types/agent.js.map +1 -1
  263. package/dist/types/ai.d.ts +3 -3
  264. package/dist/types/ai.d.ts.map +1 -1
  265. package/dist/types/errors.d.ts +15 -0
  266. package/dist/types/errors.d.ts.map +1 -0
  267. package/dist/types/errors.js +18 -0
  268. package/dist/types/errors.js.map +1 -0
  269. package/dist/types/flow.d.ts +513 -0
  270. package/dist/types/flow.d.ts.map +1 -0
  271. package/dist/types/flow.js +5 -0
  272. package/dist/types/flow.js.map +1 -0
  273. package/dist/types/index.d.ts +7 -6
  274. package/dist/types/index.d.ts.map +1 -1
  275. package/dist/types/index.js +4 -1
  276. package/dist/types/index.js.map +1 -1
  277. package/dist/types/persistence.d.ts +11 -7
  278. package/dist/types/persistence.d.ts.map +1 -1
  279. package/dist/types/routing.d.ts +1 -1
  280. package/dist/types/routing.d.ts.map +1 -1
  281. package/dist/types/session.d.ts +24 -23
  282. package/dist/types/session.d.ts.map +1 -1
  283. package/dist/types/signals.d.ts +248 -0
  284. package/dist/types/signals.d.ts.map +1 -0
  285. package/dist/types/signals.js +10 -0
  286. package/dist/types/signals.js.map +1 -0
  287. package/dist/types/template.d.ts +2 -8
  288. package/dist/types/template.d.ts.map +1 -1
  289. package/dist/types/tool.d.ts +36 -29
  290. package/dist/types/tool.d.ts.map +1 -1
  291. package/dist/types/tool.js +1 -1
  292. package/dist/types/tool.js.map +1 -1
  293. package/dist/utils/condition.d.ts +7 -1
  294. package/dist/utils/condition.d.ts.map +1 -1
  295. package/dist/utils/condition.js.map +1 -1
  296. package/dist/utils/id.d.ts +13 -5
  297. package/dist/utils/id.d.ts.map +1 -1
  298. package/dist/utils/id.js +22 -9
  299. package/dist/utils/id.js.map +1 -1
  300. package/dist/utils/index.d.ts +2 -2
  301. package/dist/utils/index.d.ts.map +1 -1
  302. package/dist/utils/index.js +2 -2
  303. package/dist/utils/index.js.map +1 -1
  304. package/dist/utils/session.d.ts +44 -5
  305. package/dist/utils/session.d.ts.map +1 -1
  306. package/dist/utils/session.js +193 -37
  307. package/dist/utils/session.js.map +1 -1
  308. package/docs/README.md +15 -202
  309. package/docs/concepts/architecture.md +281 -0
  310. package/docs/concepts/directives.md +400 -0
  311. package/docs/concepts/pipeline.md +399 -0
  312. package/docs/guides/branching.md +263 -0
  313. package/docs/guides/compaction.md +163 -0
  314. package/docs/guides/conditions.md +167 -0
  315. package/docs/guides/error-handling.md +176 -0
  316. package/docs/guides/flow-control.md +409 -0
  317. package/docs/guides/instructions.md +210 -0
  318. package/docs/guides/persistence.md +182 -0
  319. package/docs/guides/streaming.md +137 -0
  320. package/docs/migration/README.md +15 -0
  321. package/docs/migration/route-to-flow.md +560 -0
  322. package/docs/migration/v1-to-v2.md +909 -0
  323. package/docs/reference/adapters.md +481 -0
  324. package/docs/reference/branches.md +241 -0
  325. package/docs/reference/create-agent.md +186 -0
  326. package/docs/reference/directive.md +243 -0
  327. package/docs/reference/errors.md +122 -0
  328. package/docs/reference/flow.md +238 -0
  329. package/docs/reference/instruction.md +177 -0
  330. package/docs/reference/pre-directive.md +131 -0
  331. package/docs/reference/providers.md +227 -0
  332. package/docs/reference/signals.md +356 -0
  333. package/docs/reference/step.md +339 -0
  334. package/docs/reference/tool.md +269 -0
  335. package/docs/start/01-install.md +81 -0
  336. package/docs/start/02-first-agent.md +196 -0
  337. package/docs/start/03-collect-data.md +222 -0
  338. package/docs/start/04-add-tools.md +276 -0
  339. package/docs/start/05-go-to-production.md +216 -0
  340. package/examples/01-quickstart.ts +20 -0
  341. package/examples/02-data-extraction.ts +90 -0
  342. package/examples/03-tools.ts +136 -0
  343. package/examples/04-instructions.ts +100 -0
  344. package/examples/05-branching.ts +140 -0
  345. package/examples/06-flow-control.ts +103 -0
  346. package/examples/07-streaming.ts +69 -0
  347. package/examples/08-persistence.ts +98 -0
  348. package/examples/09-signals.ts +144 -0
  349. package/examples/tsconfig.json +30 -0
  350. package/package.json +2 -1
  351. package/src/adapters/MemoryAdapter.ts +3 -3
  352. package/src/adapters/MongoAdapter.ts +3 -3
  353. package/src/adapters/OpenSearchAdapter.ts +10 -8
  354. package/src/adapters/PostgreSQLAdapter.ts +26 -10
  355. package/src/adapters/PrismaAdapter.ts +6 -6
  356. package/src/adapters/RedisAdapter.ts +3 -3
  357. package/src/adapters/SQLiteAdapter.ts +31 -12
  358. package/src/constants/index.ts +2 -10
  359. package/src/core/Agent.ts +585 -374
  360. package/src/core/AutoChainExecutor.ts +440 -0
  361. package/src/core/BranchEvaluator.ts +167 -0
  362. package/src/core/DirectiveBus.ts +248 -0
  363. package/src/core/DirectiveChainTracker.ts +144 -0
  364. package/src/core/Flow.ts +666 -0
  365. package/src/core/{RoutingEngine.ts → FlowRouter.ts} +385 -365
  366. package/src/core/PersistenceManager.ts +8 -8
  367. package/src/core/PromptComposer.ts +209 -140
  368. package/src/core/PromptSectionCache.ts +1 -1
  369. package/src/core/ResponseEngine.ts +61 -46
  370. package/src/core/ResponseModal.ts +1453 -1240
  371. package/src/core/ResponsePipeline.ts +655 -175
  372. package/src/core/SignalEvaluator.ts +420 -0
  373. package/src/core/SignalProcessor.ts +723 -0
  374. package/src/core/Step.ts +279 -176
  375. package/src/core/StreamingToolExecutor.ts +4 -4
  376. package/src/core/ToolManager.ts +200 -97
  377. package/src/core/createAgent.ts +40 -0
  378. package/src/core/flow-namespace.ts +219 -0
  379. package/src/index.ts +42 -36
  380. package/src/types/agent.ts +182 -53
  381. package/src/types/ai.ts +3 -3
  382. package/src/types/errors.ts +18 -0
  383. package/src/types/flow.ts +590 -0
  384. package/src/types/index.ts +43 -16
  385. package/src/types/persistence.ts +12 -8
  386. package/src/types/routing.ts +1 -1
  387. package/src/types/session.ts +26 -23
  388. package/src/types/signals.ts +321 -0
  389. package/src/types/template.ts +3 -11
  390. package/src/types/tool.ts +50 -42
  391. package/src/utils/condition.ts +13 -4
  392. package/src/utils/id.ts +27 -9
  393. package/src/utils/index.ts +6 -2
  394. package/src/utils/session.ts +238 -42
  395. package/dist/cjs/core/BatchExecutor.d.ts +0 -359
  396. package/dist/cjs/core/BatchExecutor.d.ts.map +0 -1
  397. package/dist/cjs/core/BatchExecutor.js +0 -861
  398. package/dist/cjs/core/BatchExecutor.js.map +0 -1
  399. package/dist/cjs/core/BatchPromptBuilder.d.ts +0 -89
  400. package/dist/cjs/core/BatchPromptBuilder.d.ts.map +0 -1
  401. package/dist/cjs/core/BatchPromptBuilder.js +0 -223
  402. package/dist/cjs/core/BatchPromptBuilder.js.map +0 -1
  403. package/dist/cjs/core/Route.d.ts +0 -180
  404. package/dist/cjs/core/Route.d.ts.map +0 -1
  405. package/dist/cjs/core/Route.js +0 -542
  406. package/dist/cjs/core/Route.js.map +0 -1
  407. package/dist/cjs/core/RoutingEngine.d.ts +0 -185
  408. package/dist/cjs/core/RoutingEngine.d.ts.map +0 -1
  409. package/dist/cjs/core/RoutingEngine.js.map +0 -1
  410. package/dist/cjs/types/route.d.ts +0 -336
  411. package/dist/cjs/types/route.d.ts.map +0 -1
  412. package/dist/cjs/types/route.js.map +0 -1
  413. package/dist/core/BatchExecutor.d.ts +0 -359
  414. package/dist/core/BatchExecutor.d.ts.map +0 -1
  415. package/dist/core/BatchExecutor.js +0 -856
  416. package/dist/core/BatchExecutor.js.map +0 -1
  417. package/dist/core/BatchPromptBuilder.d.ts +0 -89
  418. package/dist/core/BatchPromptBuilder.d.ts.map +0 -1
  419. package/dist/core/BatchPromptBuilder.js +0 -219
  420. package/dist/core/BatchPromptBuilder.js.map +0 -1
  421. package/dist/core/Route.d.ts +0 -180
  422. package/dist/core/Route.d.ts.map +0 -1
  423. package/dist/core/Route.js +0 -538
  424. package/dist/core/Route.js.map +0 -1
  425. package/dist/core/RoutingEngine.d.ts +0 -185
  426. package/dist/core/RoutingEngine.d.ts.map +0 -1
  427. package/dist/core/RoutingEngine.js.map +0 -1
  428. package/dist/types/route.d.ts +0 -336
  429. package/dist/types/route.d.ts.map +0 -1
  430. package/dist/types/route.js +0 -5
  431. package/dist/types/route.js.map +0 -1
  432. package/docs/CONTRIBUTING.md +0 -521
  433. package/docs/api/README.md +0 -3299
  434. package/docs/api/overview.md +0 -1410
  435. package/docs/architecture/data-extraction-flow.md +0 -360
  436. package/docs/architecture/multi-step-execution.md +0 -277
  437. package/docs/core/agent/README.md +0 -938
  438. package/docs/core/agent/context-management.md +0 -796
  439. package/docs/core/agent/rules-and-prohibitions.md +0 -113
  440. package/docs/core/agent/session-management.md +0 -693
  441. package/docs/core/ai-integration/prompt-composition.md +0 -355
  442. package/docs/core/ai-integration/providers.md +0 -515
  443. package/docs/core/ai-integration/response-processing.md +0 -433
  444. package/docs/core/conversation-flows/data-collection.md +0 -772
  445. package/docs/core/conversation-flows/route-dsl.md +0 -509
  446. package/docs/core/conversation-flows/routes.md +0 -249
  447. package/docs/core/conversation-flows/step-transitions.md +0 -731
  448. package/docs/core/conversation-flows/steps.md +0 -268
  449. package/docs/core/error-handling.md +0 -830
  450. package/docs/core/persistence/adapters.md +0 -255
  451. package/docs/core/persistence/session-storage.md +0 -656
  452. package/docs/core/routing/intelligent-routing.md +0 -470
  453. package/docs/core/tools/enhanced-tool.md +0 -186
  454. package/docs/core/tools/streaming-execution.md +0 -161
  455. package/docs/core/tools/tool-definition.md +0 -970
  456. package/docs/core/tools/tool-scoping.md +0 -819
  457. package/docs/guides/advanced-patterns/publishing.md +0 -186
  458. package/docs/guides/context-compaction.md +0 -96
  459. package/docs/guides/error-handling-patterns.md +0 -578
  460. package/docs/guides/getting-started/README.md +0 -795
  461. package/docs/guides/migration/README.md +0 -101
  462. package/docs/guides/migration/flexible-routing-conditions.md +0 -375
  463. package/docs/guides/migration/multi-step-execution.md +0 -393
  464. package/docs/guides/migration/response-modal-refactor.md +0 -518
  465. package/docs/guides/prompt-optimization.md +0 -164
  466. package/examples/advanced-patterns/context-compaction.ts +0 -223
  467. package/examples/advanced-patterns/knowledge-based-agent.ts +0 -735
  468. package/examples/advanced-patterns/persistent-onboarding.ts +0 -728
  469. package/examples/advanced-patterns/route-lifecycle-hooks.ts +0 -556
  470. package/examples/advanced-patterns/streaming-responses.ts +0 -656
  471. package/examples/ai-providers/anthropic-integration.ts +0 -388
  472. package/examples/ai-providers/openai-integration.ts +0 -228
  473. package/examples/condition-patterns/function-only-conditions.ts +0 -365
  474. package/examples/condition-patterns/mixed-array-conditions.ts +0 -477
  475. package/examples/condition-patterns/route-skipif-patterns.ts +0 -468
  476. package/examples/condition-patterns/step-skipif-patterns.ts +0 -0
  477. package/examples/condition-patterns/string-only-conditions.ts +0 -296
  478. package/examples/conversation-flows/completion-transitions.ts +0 -318
  479. package/examples/core-concepts/basic-agent.ts +0 -503
  480. package/examples/core-concepts/modern-streaming-api.ts +0 -309
  481. package/examples/core-concepts/schema-driven-extraction.ts +0 -332
  482. package/examples/core-concepts/session-management.ts +0 -494
  483. package/examples/integrations/database-integration.ts +0 -631
  484. package/examples/integrations/healthcare-integration.ts +0 -595
  485. package/examples/integrations/search-integration.ts +0 -530
  486. package/examples/integrations/server-session-management.ts +0 -307
  487. package/examples/persistence/custom-adapter.ts +0 -526
  488. package/examples/persistence/database-persistence.ts +0 -583
  489. package/examples/persistence/memory-sessions.ts +0 -495
  490. package/examples/persistence/prisma-schema.example.prisma +0 -74
  491. package/examples/persistence/redis-persistence.ts +0 -488
  492. package/examples/tools/basic-tools.ts +0 -765
  493. package/examples/tools/data-enrichment-tools.ts +0 -593
  494. package/examples/tools/enhanced-tool-metadata.ts +0 -268
  495. package/examples/tools/streaming-tool-execution.ts +0 -283
  496. package/src/core/BatchExecutor.ts +0 -1187
  497. package/src/core/BatchPromptBuilder.ts +0 -299
  498. package/src/core/Route.ts +0 -678
  499. package/src/types/route.ts +0 -392
@@ -0,0 +1,399 @@
1
+ ---
2
+ title: "Turn pipeline"
3
+ description: "How a single call to agent.respond moves through directive resolution, routing, signals, hooks, the LLM, and persistence."
4
+ type: concept
5
+ order: 2
6
+ ---
7
+
8
+ # Turn pipeline
9
+
10
+ > **Where this is introduced:** [Architecture](./architecture.md)
11
+
12
+ Every interaction with `@falai/agent` is a *turn* — one user message in,
13
+ one assistant message out. Inside that boundary the framework runs a
14
+ fixed sequence of phases: it consumes a pending directive, evaluates
15
+ pre-signals in parallel with routing, picks the next step, runs hooks
16
+ around an LLM call, applies the merged result, and persists. The order
17
+ is the same on every turn. The shape of the turn is the framework. The
18
+ LLM understands; the pipeline keeps the code in control.
19
+
20
+ This page is the per-turn mental model: the diagram, the resolution
21
+ precedence, the per-turn **directive bus**, and the merge rules used
22
+ when more than one handler tries to write at once.
23
+
24
+ ## The pipeline diagram
25
+
26
+ ```mermaid
27
+ graph TB
28
+ IN[respond / respondStream]
29
+ IN --> PEND{session.pendingDirective?}
30
+
31
+ PEND -- yes --> APPLY1[Apply pending directive]
32
+ APPLY1 --> STEP
33
+
34
+ PEND -- no --> PAR[Parallel]
35
+ PAR --> PRE[PRE-SIGNAL phase<br/>pre / both signals]
36
+ PAR --> ROUTER[AI routing<br/>FlowRouter]
37
+
38
+ PRE --> MERGE{Pre-signal directive?}
39
+ ROUTER --> MERGE
40
+ MERGE -- halt --> HALT[Skip LLM]
41
+ MERGE -- position --> APPLY2[Apply signal position]
42
+ MERGE -- augment only --> KEEP[Keep routing + apply augmentation]
43
+ MERGE -- none --> KEEP
44
+
45
+ APPLY2 --> STEP
46
+ KEEP --> STEP
47
+
48
+ STEP[Step resolution]
49
+ STEP --> AUTO[Auto-step chain]
50
+ AUTO --> BRANCH[step.branches]
51
+ BRANCH --> SUCC[Linear successor /<br/>AI step selection]
52
+
53
+ SUCC --> ENTER[onEnter / prepare hooks<br/>pre-LLM bus]
54
+ HALT --> POSTSIG
55
+ ENTER --> LLM[LLM call + tool loop]
56
+ LLM --> FIN[finalize hook<br/>post-LLM bus]
57
+ FIN --> COL[Collect + merge directives]
58
+
59
+ COL --> POSTSIG[POST-SIGNAL phase<br/>post / both signals]
60
+ POSTSIG --> PERSIST[Persist session]
61
+ PERSIST --> OUT[AgentResponse]
62
+ ```
63
+
64
+ Three things to notice:
65
+
66
+ - **`pendingDirective` shortcuts the top half.** When a previous turn
67
+ left a directive on the session, or `agent.dispatch()` was called
68
+ between turns, it is applied first and routing is skipped.
69
+ - **Pre-signals run in parallel with routing.** Both calls are issued
70
+ via `Promise.all` so the common case (no halt, no signal redirect)
71
+ pays no extra latency. If a pre-signal halts or sets a position
72
+ field, the parallel routing result is discarded.
73
+ - **Post-signals come after the LLM.** They cannot stop *this* turn —
74
+ they observe the assistant's reply, optionally extract structured
75
+ data, and at most arm `pendingDirective` for the next turn.
76
+
77
+ The two signal phases are no-ops when `agent.signals` is empty or
78
+ unset. See [Signals](../reference/signals.md) for the full surface.
79
+
80
+ ## Resolution precedence
81
+
82
+ When more than one source could decide where the conversation goes
83
+ next — a pending directive, a pre-signal, the AI router, an auto-step,
84
+ a `step.branches` entry, the linear chain, the post-signal phase — the
85
+ pipeline resolves them in a fixed, locked order. This order does not
86
+ change between releases. Knowing it is enough to predict what every
87
+ turn will do.
88
+
89
+ 1. **`session.pendingDirective` is consumed first.**
90
+ Set on the previous turn (e.g. by a post-signal, by a
91
+ `complete: { next }` chain, by a tool that emitted `goTo`) or by
92
+ `agent.dispatch()` from outside any turn. When present it is
93
+ applied verbatim and the rest of the top half is skipped. The
94
+ field is cleared as part of the apply step so it cannot fire
95
+ twice.
96
+
97
+ 2. **PRE-SIGNAL phase, in parallel with routing.**
98
+ Pre-phase signals (`phase: 'pre'` or `phase: 'both'`) run via the
99
+ same `Promise.all` that issues the routing classifier call. Their
100
+ directives merge through the per-turn bus (see below). If the
101
+ merged pre-phase directive sets `halt: true`, the LLM is skipped
102
+ for this turn. If it carries a position field (`goTo`,
103
+ `goToStep`, `complete`, `abort`, `reset`), that position wins and
104
+ the routing result is discarded. If it only carries augmentation
105
+ (`appendPrompt`, `injectTools`) or state writes (`dataUpdate`,
106
+ `contextUpdate`), routing is kept and the augmentation is layered
107
+ on top.
108
+
109
+ 3. **AI routing.**
110
+ `FlowRouter.decide` picks the active flow and entry step based on
111
+ the user message, conversation history, and each flow's `when`
112
+ condition. Used only when steps 1 and 2 produced no position
113
+ field. Routing is the framework's *intent classifier*: it answers
114
+ "what is the user trying to do right now?" and nothing else. It
115
+ never writes data and never speaks.
116
+
117
+ 4. **Auto-step chain.**
118
+ With a current flow and step in hand, the pipeline walks the
119
+ `auto: true` chain — each auto-step's `onEnter` and `prepare`
120
+ hooks fire, branches resolve, and the chain advances without an
121
+ LLM call until it reaches a non-auto step, a `halt`, a `reply`,
122
+ a `complete`, or the per-turn cap (`maxAutoStepsPerTurn`). Auto
123
+ steps are how the code half of the contract advances state in
124
+ bulk between user messages.
125
+
126
+ 5. **Step branches.**
127
+ When the resolved step has `branches`, they evaluate in
128
+ declaration order. The `if` predicate runs first (free, code-only
129
+ evaluation). If it passes, the optional `when` string is sent to
130
+ the AI as a yes/no classifier. The first entry whose conditions
131
+ all match wins; its `then` is applied — a step id (jump within
132
+ the flow), a flow id (cross-flow jump), or a full `Directive`.
133
+ Code-only branches incur zero token cost.
134
+
135
+ 6. **Linear successor / AI step selection.**
136
+ When no branch matched, the pipeline falls through to the linear
137
+ chain. If exactly one candidate successor passes its `skip`
138
+ condition, that step is entered. If several pass, the framework
139
+ asks the AI to pick (the same routing primitive, scoped to the
140
+ surviving candidates). If none pass, the flow is implicitly
141
+ complete (the *last step terminates the flow* rule from v2 — no
142
+ sentinel value, no special return type).
143
+
144
+ 7. **POST-SIGNAL phase.**
145
+ After `finalize` and the post-LLM bus merge, post-phase signals
146
+ evaluate sequentially against the just-completed turn. They see
147
+ the assistant's reply, the collected data, and any tool results.
148
+ Post-phase position directives (`goTo`, `goToStep`, `complete`)
149
+ set `session.pendingDirective` for the *next* turn — there is no
150
+ mid-turn re-entry, by design, to keep turn semantics legible.
151
+ Pre-LLM-only fields (`appendPrompt`, `injectTools`, `halt`) are
152
+ dropped with a debug warning if a post-phase signal emits them.
153
+
154
+ This is the precedence the rest of the framework is built around. The
155
+ [Resolution precedence section](../reference/signals.md#resolution-precedence-within-a-turn)
156
+ on the signals reference page restates the same list as a contract;
157
+ this concept page is the prose explanation.
158
+
159
+ ## The directive bus
160
+
161
+ Hooks, tools, and signal handlers all express their intent the same
162
+ way: by emitting a [`Directive`](../reference/directive.md). The
163
+ pipeline collects these emissions into a per-turn, in-memory **bus**
164
+ and reduces them to a single applied directive at the end of each
165
+ phase. Two phases, one merge function.
166
+
167
+ ### Pre-LLM phase
168
+
169
+ Sources, in fixed order:
170
+
171
+ 1. `agent.hooks.onEnter` (if the agent supports hooks at this level)
172
+ 2. `flow.hooks.onEnter` for the active flow
173
+ 3. `step.hooks.onEnter` for the resolved step
174
+ 4. `step.hooks.prepare`
175
+ 5. Any `ctx.dispatch` calls made inside the above hooks
176
+
177
+ These return `PreDirective` — the same shape as `Directive` plus the
178
+ three pre-LLM-only fields (`appendPrompt`, `injectTools`, `halt`). The
179
+ merged result feeds the prompt composer and tool manager before the
180
+ LLM call:
181
+
182
+ - `halt: true` skips the LLM entirely (a `reply`, if any, becomes the
183
+ literal assistant message).
184
+ - `appendPrompt[]` is concatenated into the system prompt for this
185
+ turn only.
186
+ - `injectTools[]` is added to the available tool list for this turn
187
+ only.
188
+ - Position fields, state writes, and `reply` carry forward exactly
189
+ as they would from any directive.
190
+
191
+ ### Post-LLM phase
192
+
193
+ Sources, in fixed order:
194
+
195
+ 1. Each `ToolResult.directive` returned during the tool loop
196
+ 2. Any `ctx.dispatch` calls inside tool handlers
197
+ 3. `step.hooks.finalize`
198
+ 4. `flow.hooks.onComplete` (when the flow finished this turn)
199
+ 5. The `then` branch of any matched `step.branches` entry whose `then`
200
+ was a full `Directive` (not just a step id)
201
+
202
+ These return plain `Directive`. PreDirective-only fields here are
203
+ dropped with a debug warning — `halt` after the fact has no meaning,
204
+ and `appendPrompt` / `injectTools` could not influence a call that
205
+ has already happened.
206
+
207
+ ### Why a bus
208
+
209
+ Two reasons. First, multiple emitters are normal: a `prepare` hook
210
+ might add a sentence to the prompt while a tool result writes
211
+ collected data while a `finalize` hook completes the flow. The bus
212
+ makes "who wrote what" explicit and the merge rules deterministic.
213
+ Second, observability: `AgentResponse.directiveChain` returns the full
214
+ list of emitted directives in order with their sources, so traces
215
+ explain themselves without bisecting hook code.
216
+
217
+ The bus is purely in-memory and lasts one turn. Nothing about the bus
218
+ itself is persisted; only the *applied* directive's effects (state
219
+ writes, position changes, `pendingDirective` for the next turn) cross
220
+ the persistence boundary.
221
+
222
+ ## Algorithm 4 — merge rules
223
+
224
+ When the bus has more than one directive in a phase, the pipeline
225
+ folds them into a single `Directive` (or `PreDirective` in the pre-LLM
226
+ phase) using the following rules. Same rules in both phases; the
227
+ pre-LLM phase additionally folds the three augmentation fields.
228
+
229
+ ### Position fields — winner-takes-all by precedence
230
+
231
+ Exactly one position field can apply per phase. The winner is chosen
232
+ by the precedence:
233
+
234
+ ```
235
+ abort > complete > goTo / goToStep > reset
236
+ ```
237
+
238
+ - **`abort` always wins.** A handler that aborts the conversation
239
+ cannot be overridden by a later "go somewhere else" — there is no
240
+ somewhere else.
241
+ - **`complete` beats `goTo` / `goToStep`.** The flow is ending; any
242
+ follow-up jump belongs in `complete.next`, not as a competing
243
+ position field.
244
+ - **`goTo` and `goToStep` share a tier.** `goTo` is the cross-flow
245
+ hop; `goToStep` is the within-flow hop. Both express "next position
246
+ is here." Among same-tier emissions, last-wins.
247
+ - **`reset` is lowest.** Any explicit jump out of the current flow
248
+ beats a "restart this flow" emitted earlier in the phase.
249
+
250
+ Within the same precedence tier, **last emission wins**. The pipeline
251
+ logs a debug-level warning naming all conflicting sources so a noisy
252
+ turn is diagnosable.
253
+
254
+ ### `reply` — last-wins
255
+
256
+ `reply` is a verbatim assistant utterance — the LLM is bypassed for
257
+ the message body. If two emitters set `reply`, the second wins. The
258
+ pipeline logs a debug warning so the override is visible.
259
+
260
+ `reply` and `abort` are mutually exclusive at apply time: an aborted
261
+ conversation cannot deliver a reply, and the pipeline rejects the
262
+ combination as a `FlowConfigurationError`.
263
+
264
+ ### `dataUpdate` and `contextUpdate` — shallow-merge in emit order
265
+
266
+ State writes are *additive*: every emitter contributes its slice and
267
+ the merger shallow-merges them in declaration order, last write wins
268
+ on key collision. The `Object.assign({}, a, b)` semantics — top-level
269
+ keys overwrite, nested objects are not deep-merged.
270
+
271
+ This is the rule that lets a flow-level `onEnter` set a default while
272
+ a step-level `prepare` overrides one field on top, without the two
273
+ hooks needing to know about each other.
274
+
275
+ After merging, the combined `dataUpdate` is validated against
276
+ `agent.schema` *atomically* — every field across every emitter is
277
+ checked together, and the session is not mutated unless the whole set
278
+ passes. A failure throws `DataValidationError` with the offending
279
+ field and emitter listed.
280
+
281
+ ### `appendPrompt` and `injectTools` — concatenate, then dedupe
282
+
283
+ Pre-LLM only. Both fields are arrays; the merger concatenates them in
284
+ emit order and then deduplicates:
285
+
286
+ - **`appendPrompt`** is concatenated and rendered into the prompt's
287
+ per-turn appendage slot. No deduplication — duplicates from
288
+ different sources are preserved (a flow-level "be polite" plus a
289
+ step-level "be polite" is acceptable redundancy).
290
+ - **`injectTools`** is concatenated and deduped by `Tool.id`. When
291
+ two emitters inject a tool with the same id, the *later* definition
292
+ wins — typically a step-level injection overriding a flow-level
293
+ default.
294
+
295
+ Both arrays apply only for this turn; they are stripped before
296
+ `session.pendingDirective` is written, and they cannot be persisted.
297
+
298
+ ### `halt` — logical-OR
299
+
300
+ Pre-LLM only. If *any* pre-phase emitter set `halt: true`, the merged
301
+ directive halts. There is no "vote" — a single emitter is enough. The
302
+ LLM is not called; if a `reply` is also set, that becomes the literal
303
+ assistant message; otherwise the turn ends with an empty body and
304
+ `stoppedReason: 'halt'`.
305
+
306
+ `halt` is the framework's circuit breaker: a hook that detects an
307
+ unresolvable state can stop the turn outright without competing with
308
+ other emitters.
309
+
310
+ ## One turn end-to-end
311
+
312
+ The same shape, traced as a sequence. Lanes are *User*, *Agent*
313
+ (`agent.respond`), *Pipeline* (the internal turn pipeline),
314
+ *Provider* (the AI), and *Adapter* (the persistence layer).
315
+
316
+ ```mermaid
317
+ sequenceDiagram
318
+ participant User
319
+ participant Agent
320
+ participant Pipeline
321
+ participant Provider
322
+ participant Adapter
323
+
324
+ User->>Agent: respond("I want to book a hotel")
325
+ Agent->>Adapter: load session
326
+ Adapter-->>Agent: SessionState (with pendingDirective?)
327
+
328
+ alt session.pendingDirective is set
329
+ Agent->>Pipeline: applyDirective(pendingDirective)
330
+ Pipeline->>Pipeline: clear pendingDirective
331
+ else
332
+ par Parallel
333
+ Agent->>Pipeline: runPreSignalPhase()
334
+ Pipeline->>Provider: classifier call (batched signals)
335
+ Provider-->>Pipeline: matched + extracted
336
+ and
337
+ Agent->>Pipeline: FlowRouter.decide()
338
+ Pipeline->>Provider: routing call
339
+ Provider-->>Pipeline: { flow, step }
340
+ end
341
+ Pipeline->>Pipeline: merge pre-signal bus<br/>halt? position? augment? none?
342
+ end
343
+
344
+ Pipeline->>Pipeline: walk auto-step chain
345
+ Pipeline->>Pipeline: evaluate step.branches
346
+ Pipeline->>Pipeline: select successor / linear chain
347
+
348
+ Pipeline->>Pipeline: onEnter + prepare hooks<br/>(pre-LLM bus)
349
+ Pipeline->>Provider: generate(prompt + tools + history)
350
+
351
+ loop tool loop
352
+ Provider-->>Pipeline: tool call
353
+ Pipeline->>Pipeline: ToolManager.execute<br/>(may emit Directive)
354
+ Pipeline->>Provider: tool result
355
+ end
356
+
357
+ Provider-->>Pipeline: assistant message
358
+ Pipeline->>Pipeline: finalize hook<br/>(post-LLM bus)
359
+ Pipeline->>Pipeline: collect + merge directives<br/>(Algorithm 4)
360
+ Pipeline->>Pipeline: applyDirective(merged)
361
+
362
+ Pipeline->>Pipeline: runPostSignalPhase()
363
+ Pipeline->>Provider: extraction call (if signals declared)
364
+ Provider-->>Pipeline: matched + extracted
365
+ Pipeline->>Pipeline: post-phase position?<br/>arm pendingDirective for next turn
366
+
367
+ Pipeline->>Adapter: persist session
368
+ Pipeline-->>Agent: AgentResponse
369
+ Agent-->>User: assistant message
370
+ ```
371
+
372
+ A few things this diagram makes precise that the high-level graph
373
+ elides:
374
+
375
+ - The session is loaded *before* the pending-directive check, because
376
+ `pendingDirective` lives on the session itself.
377
+ - The pre-signal classifier call and the routing call are issued in
378
+ parallel; the merge step decides whether the routing result is used
379
+ or discarded.
380
+ - The tool loop is internal to the LLM phase. Tools that emit
381
+ directives feed the *post-LLM* bus, not the pre-LLM one — they ran
382
+ during a call, not before it.
383
+ - The post-signal phase happens *after* directive apply, before
384
+ persistence. That's the only window in which post-phase handlers
385
+ can see the fully-applied turn state.
386
+ - Persistence is the last write. Anything that did not survive the
387
+ applied directive (the bus contents, the pre-LLM augmentation
388
+ arrays, `halt`) is gone by the time the adapter is called.
389
+
390
+ ## Where to go next
391
+
392
+ The pipeline is the *what happens*. The directive is the *how
393
+ handlers ask for things to happen*. The next concept page covers the
394
+ flat shape, the position field rules, the inheritance chain
395
+ `Directive → PreDirective → SignalDirective`, and the `flow`
396
+ namespace helpers (`flow.isDirective`, `flow.merge`, `flow.validate`)
397
+ that make the bus introspectable from user code.
398
+
399
+ **Next:** [Directives](./directives.md)
@@ -0,0 +1,263 @@
1
+ ---
2
+ title: "Branching"
3
+ description: "Add an explicit, source-local fork to a step with `branches`, mixing code predicates and AI conditions and pointing at step ids, flow ids, or full directives."
4
+ type: guide
5
+ order: 2
6
+ ---
7
+
8
+ # Branching
9
+
10
+ Most flows are linear: ask, collect, confirm, complete. A few aren't. Sometimes the conversation arrives at a step that has to choose between three or four next moves — and the choice is the point of the step, not an aside. This guide shows how to express that fork with `step.branches`.
11
+
12
+ You already know the [`when` / `if` split](./conditions.md): `when` is an AI-evaluated string, `if` is a code predicate, and the two compose so that code runs first for free and the AI only fires when the predicate already passed. `branches` reuses that vocabulary in a list-shaped form. Each entry says "here is a possible next step, and here is how to choose it." Entries evaluate top-to-bottom; the first one whose conditions pass wins.
13
+
14
+ By the end of this page you will have written four branch points: a pure-code fork that costs zero tokens, an AI-only fork that classifies intent, a combined fork that short-circuits the LLM call, and a cross-flow fork that hands control to a different flow with state carry.
15
+
16
+ ## The shape
17
+
18
+ `step.branches` is an array of `BranchEntry` objects. Each entry has up to four fields:
19
+
20
+ ```typescript
21
+ interface BranchEntry<TContext, TData> {
22
+ /** AI-evaluated condition. String or array of strings (AND). */
23
+ when?: string | string[];
24
+
25
+ /** Code predicate. Function or array of functions (AND). */
26
+ if?: BranchPredicate<TContext, TData> | BranchPredicate<TContext, TData>[];
27
+
28
+ /**
29
+ * Where to go when this entry matches. One of:
30
+ * - A string matching a step id in the current flow.
31
+ * - A string matching a flow id (sugar for `{ goTo: <flowId> }`).
32
+ * - A full Directive (cross-flow step targets, state writes, completion).
33
+ */
34
+ then: string | Directive<TContext, TData>;
35
+
36
+ /** Optional label surfaced in event traces and flow visualization. */
37
+ label?: string;
38
+ }
39
+ ```
40
+
41
+ Three rules govern how a `branches` array is read:
42
+
43
+ 1. **Declaration order is evaluation order.** The first entry whose conditions pass wins. Later entries don't run.
44
+ 2. **Code-first short-circuit.** When an entry has both `if` and `when`, `if` runs first. `when` is only evaluated when `if` passes — saving the LLM call when the predicate already disqualifies the entry.
45
+ 3. **An entry with neither `when` nor `if` is an unconditional fallback.** It's only legal as the last entry in the array. Putting it earlier is a `FlowConfigurationError` because every later entry would be unreachable.
46
+
47
+ If no entry matches, branches return `undefined` and resolution falls through to the linear `nextStep` chain or AI step selection — exactly as if `branches` were absent. That is the same fall-through that backs the implicit-fork pattern, so the two forms compose cleanly.
48
+
49
+ ## A code-only fork
50
+
51
+ The cheapest fork is one that doesn't call the LLM at all. When the choice is purely a function of `data` and `context`, every entry uses `if` and the whole decision runs in pure TypeScript.
52
+
53
+ ```typescript
54
+ flow({
55
+ id: "plan_routing",
56
+ steps: [
57
+ {
58
+ id: "route_by_plan",
59
+ auto: true,
60
+ branches: [
61
+ { if: ({ data }) => data.plan === "enterprise", then: "enterprise_path", label: "enterprise" },
62
+ { if: ({ data }) => data.plan === "pro", then: "pro_path", label: "pro" },
63
+ { then: "free_path" }, // unconditional fallback (last entry)
64
+ ],
65
+ },
66
+ { id: "enterprise_path", prompt: "A specialist will reach out." },
67
+ { id: "pro_path", prompt: "Set up your pro account." },
68
+ { id: "free_path", prompt: "Welcome to the free tier." },
69
+ ],
70
+ });
71
+ ```
72
+
73
+ Two things matter here. First, `auto: true` on the source step removes the LLM call that would otherwise run for `route_by_plan` itself — the step asks no question, only routes. Combined with code-only branches, the entire decision is a pure compute node in the graph. Second, the unconditional fallback at the end is what guarantees a target. Without it, a `data.plan` value the engine doesn't know about would fall through to linear succession instead of landing on `free_path`.
74
+
75
+ The `BranchPredicate` receives `{ data, context, session, history }`. `data` is `Partial<TData>` — predicates have to null-check any field not declared in the source step's `requires`. Fields covered by `requires` are guaranteed present.
76
+
77
+ ## An AI-only fork
78
+
79
+ Some forks need intent classification. The user said something; the agent has to decide whether they want to cancel, ask about billing, or get technical help. None of that lives in `data` yet, and writing a regex would lose the point of the framework.
80
+
81
+ ```typescript
82
+ flow({
83
+ id: "support",
84
+ steps: [
85
+ {
86
+ id: "classify_request",
87
+ prompt: "How can I help?",
88
+ branches: [
89
+ { when: "user wants to cancel their account", then: "cancel_flow" },
90
+ { when: "user is asking about billing", then: "billing_flow" },
91
+ { when: "user is asking a technical question", then: "tech_support" },
92
+ { then: "general_help" }, // fallback
93
+ ],
94
+ },
95
+ { id: "tech_support", prompt: "What are you running into?" },
96
+ { id: "general_help", prompt: "I can help with that." },
97
+ // cancel_flow and billing_flow are top-level flows resolved via goTo.
98
+ ],
99
+ });
100
+ ```
101
+
102
+ `when` strings reuse the same machinery as `step.when`. One LLM call evaluates each entry's condition in declaration order; the first match wins. Conditions are written from the agent's perspective — `"user wants to cancel"` reads naturally and matches the prompts the model already speaks. Don't try to compress them into keywords; the AI is doing classification, not pattern matching.
103
+
104
+ The fallback at the end is doing real work too. When the model can't classify the message into any of the three buckets — say the user typed "hi" — `general_help` catches it instead of dropping the user into AI step selection.
105
+
106
+ ## Combining `if` and `when`
107
+
108
+ When an entry has both fields set, code runs first and the AI call only fires when the predicate already passed. This is the same short-circuit you saw in the [conditions guide](./conditions.md), now scoped to a single branch entry.
109
+
110
+ ```typescript
111
+ flow({
112
+ id: "pricing",
113
+ steps: [
114
+ {
115
+ id: "pricing_routing",
116
+ branches: [
117
+ {
118
+ // Free predicate runs first; AI condition only fires when it passes.
119
+ if: ({ data, context }) =>
120
+ data.country === "US" && context.featureFlags.enableUsPricing,
121
+ when: "user is asking about pricing",
122
+ then: "us_pricing",
123
+ },
124
+ {
125
+ when: "user is asking about pricing",
126
+ then: "global_pricing",
127
+ },
128
+ { then: "general_help" }, // fallback
129
+ ],
130
+ },
131
+ { id: "us_pricing", prompt: "Here is US pricing." },
132
+ { id: "global_pricing", prompt: "Here is global pricing." },
133
+ { id: "general_help", prompt: "I can help with that." },
134
+ ],
135
+ });
136
+ ```
137
+
138
+ For users outside the US — or with the feature flag off — `if` returns `false` immediately and the entry is skipped without ever evaluating `when`. The second entry then evaluates `when` once and routes to `global_pricing`. A US user with the flag on passes `if`, and only then does the AI call evaluate "user is asking about pricing." When both pass, the entry wins.
139
+
140
+ The cost difference is real. Without the short-circuit, every turn would pay one LLM call to evaluate `when` for every entry that names it. With it, the framework spends tokens on the entries it can't dispose of for free.
141
+
142
+ ## Resolving `then`
143
+
144
+ `then` accepts three forms, resolved in this order:
145
+
146
+ 1. **A step id in the current flow** — the engine enters that step directly. Same effect as `nextStep: <id>` would have on a linear chain.
147
+ 2. **A flow id** — sugar for `applyDirective({ goTo: <flowId> })`. The string is desugared into a flow transition with no data carry.
148
+ 3. **A `Directive` object** — applied via `applyDirective` directly. This is the form to use whenever you need anything more than a bare jump: cross-flow steps, data writes, completion, or verbatim replies.
149
+
150
+ When a string matches both a local step id and a flow id (rare; you'd have to name them the same thing), the local step wins. Step ids are scoped to the current flow, so the lookup is unambiguous.
151
+
152
+ A bare string **never** resolves to a step in a different flow, even when that step id is globally unique. Cross-flow step targets require the `Directive` form:
153
+
154
+ ```typescript
155
+ {
156
+ if: ({ data }) => data.escalate === true,
157
+ then: { goToStep: { step: "priority_intake", flow: "Escalation" } },
158
+ }
159
+ ```
160
+
161
+ This is intentional. Keeping the string-form lookup confined to "current flow's steps or any flow id" means a glance at the source tells you what the string means without needing to know whether some unrelated flow happens to define a step by the same name. When you mean to cross flows, the `Directive` form makes that explicit at the call site.
162
+
163
+ ## A mixed-target router
164
+
165
+ Most real branch points combine forms. One entry tests data, another classifies intent, a third writes state and completes. The `branches` array reads top-to-bottom as a list of cases:
166
+
167
+ ```typescript
168
+ flow({
169
+ id: "router",
170
+ steps: [
171
+ {
172
+ id: "classify",
173
+ prompt: "How can I help?",
174
+ branches: [
175
+ // Step id in the current flow.
176
+ { if: ({ data }) => data.tier === "enterprise", then: "enterprise_path" },
177
+
178
+ // Flow id — sugar for { goTo: "CancellationFlow" }.
179
+ { when: "user wants to cancel", then: "CancellationFlow" },
180
+
181
+ // Full Directive — cross-flow with data carry.
182
+ {
183
+ when: "user is asking about a refund",
184
+ then: { goTo: { flow: "Refund", data: { source: "classify" } } },
185
+ },
186
+
187
+ // Cross-flow step reference (Directive required).
188
+ {
189
+ if: ({ data }) => data.escalate === true,
190
+ then: { goToStep: { step: "priority_intake", flow: "Escalation" } },
191
+ },
192
+
193
+ // Directive with completion + state write.
194
+ {
195
+ if: ({ data }) => data.shouldComplete === true,
196
+ then: { complete: true, dataUpdate: { closedAt: new Date().toISOString() } },
197
+ },
198
+
199
+ // Unconditional fallback.
200
+ { then: "default_path" },
201
+ ],
202
+ },
203
+ { id: "enterprise_path", prompt: "..." },
204
+ { id: "default_path", prompt: "..." },
205
+ ],
206
+ });
207
+ ```
208
+
209
+ The mix is the point. Code-only entries pay nothing, AI entries pay one classification, the Directive entries reach across flows or write completion state. Everything declarative is in one list at the source step. There is no separate router file, no decision tree spread across `step.when` clauses on five different successors.
210
+
211
+ ## Coexisting with the implicit fork
212
+
213
+ Branches don't replace the implicit-fork pattern (multiple successor steps each carrying their own `step.when`). Both forms stay first-class. Choose by intent:
214
+
215
+ - **Implicit fork** when the flow reads as a linear chain that occasionally skips. The fork is incidental — the bulk of the flow is "do A, then B, then maybe C, then D."
216
+ - **Explicit fork (`branches`)** when the source step is a routing point. The fork is the point — the step exists to choose between N targets and the targets are owned by the source.
217
+
218
+ When both are present on the same step, branches take precedence. If a branch entry matches, the linear chain is bypassed entirely. If `branches` returns `undefined` — no entry matched — the linear chain runs as if `branches` were absent.
219
+
220
+ ```typescript
221
+ flow({
222
+ id: "support",
223
+ steps: [
224
+ {
225
+ id: "intake",
226
+ prompt: "How can I help?",
227
+ // Branches handle the obvious cases up-front.
228
+ branches: [
229
+ { if: ({ data }) => data.priority === "P0", then: "fast_path" },
230
+ { when: "user is asking a billing question", then: "billing" },
231
+ ],
232
+ },
233
+ // Linear successors with their own `when` — the implicit-fork pattern.
234
+ // These only run if no branch entry matched on `intake`.
235
+ { id: "tech", when: "user is asking a technical question", prompt: "..." },
236
+ { id: "general", prompt: "I can help with that." },
237
+ // ...
238
+ { id: "fast_path", prompt: "..." },
239
+ { id: "billing", prompt: "..." },
240
+ ],
241
+ });
242
+ ```
243
+
244
+ The combination here is deliberate. Branches catch the cases that have an explicit short-circuit (P0 priority is in `data`, billing is unambiguous to classify). Everything else falls through to the implicit fork, which the AI step selector picks among. Neither pattern is forced into doing what the other does better.
245
+
246
+ ## What you get back
247
+
248
+ When a branch entry's `then` is applied, the response surfaces it the same way it would surface any other position decision. The active step changes, `currentStep.id` reflects the new step, and `appliedInstructions` is recomputed for the new scope. If the entry's `then` was a `Directive` that included `dataUpdate` or `contextUpdate`, those writes are visible on the response too — branches participate in state writes through the same `applyDirective` machinery as tools and hooks.
249
+
250
+ The optional `label` field on each entry doesn't affect resolution; it's surfaced in event traces and in flow visualization tools so you can see which branch fired without having to read the predicate. Use it on entries whose `if`/`when` conditions are long enough to be hard to skim at a glance.
251
+
252
+ ## Recap
253
+
254
+ Four moves cover almost every fork you'll write:
255
+
256
+ - **Code-only** when the choice is in `data` or `context`. Free, deterministic, and combinable with `auto: true` for zero-LLM routing nodes.
257
+ - **AI-only** when the choice is intent classification. One LLM call, declared at the source step instead of scattered across target-step `when` clauses.
258
+ - **Combined `if + when`** when both must agree. Code runs first, AI runs only when code passed.
259
+ - **Directive in `then`** when the target is in another flow, when state needs to be written, or when the branch should complete or abort the flow.
260
+
261
+ `branches` doesn't add a new control-flow primitive. It reuses `when`, `if`, and `Directive` in a list shape that makes the fork visible at the source step. Pair it with the implicit-fork pattern when the linear chain is the natural read; pair it with `auto: true` when the routing decision shouldn't speak.
262
+
263
+ **Next:** [Flow control](./flow-control.md)