@falai/agent 2.6.1 → 3.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 (611) hide show
  1. package/README.md +2 -2
  2. package/dist/adapters/MemoryAdapter.d.ts +1 -1
  3. package/dist/adapters/MemoryAdapter.d.ts.map +1 -1
  4. package/dist/adapters/MemoryAdapter.js +32 -36
  5. package/dist/adapters/MemoryAdapter.js.map +1 -1
  6. package/dist/adapters/MongoAdapter.d.ts +1 -1
  7. package/dist/adapters/MongoAdapter.d.ts.map +1 -1
  8. package/dist/adapters/MongoAdapter.js +2 -2
  9. package/dist/adapters/MongoAdapter.js.map +1 -1
  10. package/dist/adapters/OpenSearchAdapter.d.ts +1 -1
  11. package/dist/adapters/OpenSearchAdapter.d.ts.map +1 -1
  12. package/dist/adapters/OpenSearchAdapter.js +3 -3
  13. package/dist/adapters/OpenSearchAdapter.js.map +1 -1
  14. package/dist/adapters/PostgreSQLAdapter.d.ts +1 -1
  15. package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -1
  16. package/dist/adapters/PostgreSQLAdapter.js +18 -13
  17. package/dist/adapters/PostgreSQLAdapter.js.map +1 -1
  18. package/dist/adapters/PrismaAdapter.d.ts +1 -1
  19. package/dist/adapters/PrismaAdapter.d.ts.map +1 -1
  20. package/dist/adapters/PrismaAdapter.js +3 -3
  21. package/dist/adapters/PrismaAdapter.js.map +1 -1
  22. package/dist/adapters/RedisAdapter.d.ts +2 -1
  23. package/dist/adapters/RedisAdapter.d.ts.map +1 -1
  24. package/dist/adapters/RedisAdapter.js +77 -27
  25. package/dist/adapters/RedisAdapter.js.map +1 -1
  26. package/dist/adapters/SQLiteAdapter.d.ts +1 -1
  27. package/dist/adapters/SQLiteAdapter.d.ts.map +1 -1
  28. package/dist/adapters/SQLiteAdapter.js +9 -31
  29. package/dist/adapters/SQLiteAdapter.js.map +1 -1
  30. package/dist/adapters/index.d.ts +13 -13
  31. package/dist/adapters/index.d.ts.map +1 -1
  32. package/dist/adapters/index.js +7 -7
  33. package/dist/adapters/index.js.map +1 -1
  34. package/dist/adapters/sessionRow.d.ts +22 -0
  35. package/dist/adapters/sessionRow.d.ts.map +1 -0
  36. package/dist/adapters/sessionRow.js +48 -0
  37. package/dist/adapters/sessionRow.js.map +1 -0
  38. package/dist/cjs/adapters/MemoryAdapter.d.ts +1 -1
  39. package/dist/cjs/adapters/MemoryAdapter.d.ts.map +1 -1
  40. package/dist/cjs/adapters/MemoryAdapter.js +39 -43
  41. package/dist/cjs/adapters/MemoryAdapter.js.map +1 -1
  42. package/dist/cjs/adapters/MongoAdapter.d.ts +1 -1
  43. package/dist/cjs/adapters/MongoAdapter.d.ts.map +1 -1
  44. package/dist/cjs/adapters/MongoAdapter.js +4 -4
  45. package/dist/cjs/adapters/MongoAdapter.js.map +1 -1
  46. package/dist/cjs/adapters/OpenSearchAdapter.d.ts +1 -1
  47. package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +1 -1
  48. package/dist/cjs/adapters/OpenSearchAdapter.js +6 -6
  49. package/dist/cjs/adapters/OpenSearchAdapter.js.map +1 -1
  50. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +1 -1
  51. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +1 -1
  52. package/dist/cjs/adapters/PostgreSQLAdapter.js +20 -15
  53. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -1
  54. package/dist/cjs/adapters/PrismaAdapter.d.ts +1 -1
  55. package/dist/cjs/adapters/PrismaAdapter.d.ts.map +1 -1
  56. package/dist/cjs/adapters/PrismaAdapter.js +7 -7
  57. package/dist/cjs/adapters/PrismaAdapter.js.map +1 -1
  58. package/dist/cjs/adapters/RedisAdapter.d.ts +2 -1
  59. package/dist/cjs/adapters/RedisAdapter.d.ts.map +1 -1
  60. package/dist/cjs/adapters/RedisAdapter.js +81 -31
  61. package/dist/cjs/adapters/RedisAdapter.js.map +1 -1
  62. package/dist/cjs/adapters/SQLiteAdapter.d.ts +1 -1
  63. package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +1 -1
  64. package/dist/cjs/adapters/SQLiteAdapter.js +11 -33
  65. package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -1
  66. package/dist/cjs/adapters/index.d.ts +13 -13
  67. package/dist/cjs/adapters/index.d.ts.map +1 -1
  68. package/dist/cjs/adapters/index.js +14 -14
  69. package/dist/cjs/adapters/index.js.map +1 -1
  70. package/dist/cjs/adapters/sessionRow.d.ts +22 -0
  71. package/dist/cjs/adapters/sessionRow.d.ts.map +1 -0
  72. package/dist/cjs/adapters/sessionRow.js +52 -0
  73. package/dist/cjs/adapters/sessionRow.js.map +1 -0
  74. package/dist/cjs/core/Agent.d.ts +19 -11
  75. package/dist/cjs/core/Agent.d.ts.map +1 -1
  76. package/dist/cjs/core/Agent.js +102 -64
  77. package/dist/cjs/core/Agent.js.map +1 -1
  78. package/dist/cjs/core/AutoChainExecutor.d.ts +12 -22
  79. package/dist/cjs/core/AutoChainExecutor.d.ts.map +1 -1
  80. package/dist/cjs/core/AutoChainExecutor.js +46 -49
  81. package/dist/cjs/core/AutoChainExecutor.js.map +1 -1
  82. package/dist/cjs/core/BranchEvaluator.d.ts +4 -4
  83. package/dist/cjs/core/BranchEvaluator.d.ts.map +1 -1
  84. package/dist/cjs/core/BranchEvaluator.js +6 -6
  85. package/dist/cjs/core/BranchEvaluator.js.map +1 -1
  86. package/dist/cjs/core/CompactionEngine.d.ts +16 -3
  87. package/dist/cjs/core/CompactionEngine.d.ts.map +1 -1
  88. package/dist/cjs/core/CompactionEngine.js +30 -6
  89. package/dist/cjs/core/CompactionEngine.js.map +1 -1
  90. package/dist/cjs/core/DirectiveChainTracker.d.ts +1 -1
  91. package/dist/cjs/core/DirectiveChainTracker.d.ts.map +1 -1
  92. package/dist/cjs/core/DirectiveChainTracker.js +5 -5
  93. package/dist/cjs/core/DirectiveChainTracker.js.map +1 -1
  94. package/dist/cjs/core/Events.d.ts +1 -1
  95. package/dist/cjs/core/Events.d.ts.map +1 -1
  96. package/dist/cjs/core/Events.js +15 -15
  97. package/dist/cjs/core/Events.js.map +1 -1
  98. package/dist/cjs/core/Flow.d.ts +4 -4
  99. package/dist/cjs/core/Flow.d.ts.map +1 -1
  100. package/dist/cjs/core/Flow.js +15 -15
  101. package/dist/cjs/core/Flow.js.map +1 -1
  102. package/dist/cjs/core/FlowRouter.d.ts +4 -4
  103. package/dist/cjs/core/FlowRouter.d.ts.map +1 -1
  104. package/dist/cjs/core/FlowRouter.js +92 -70
  105. package/dist/cjs/core/FlowRouter.js.map +1 -1
  106. package/dist/cjs/core/PersistenceManager.d.ts +1 -1
  107. package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
  108. package/dist/cjs/core/PersistenceManager.js +10 -10
  109. package/dist/cjs/core/PersistenceManager.js.map +1 -1
  110. package/dist/cjs/core/PromptComposer.d.ts +4 -4
  111. package/dist/cjs/core/PromptComposer.d.ts.map +1 -1
  112. package/dist/cjs/core/PromptComposer.js +13 -13
  113. package/dist/cjs/core/PromptComposer.js.map +1 -1
  114. package/dist/cjs/core/PromptSectionCache.d.ts +2 -2
  115. package/dist/cjs/core/PromptSectionCache.d.ts.map +1 -1
  116. package/dist/cjs/core/ResponseEngine.d.ts +4 -4
  117. package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
  118. package/dist/cjs/core/ResponseEngine.js +7 -7
  119. package/dist/cjs/core/ResponseEngine.js.map +1 -1
  120. package/dist/cjs/core/ResponseGenerationError.d.ts.map +1 -1
  121. package/dist/cjs/core/ResponseGenerationError.js +3 -5
  122. package/dist/cjs/core/ResponseGenerationError.js.map +1 -1
  123. package/dist/cjs/core/ResponseModal.d.ts +39 -10
  124. package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
  125. package/dist/cjs/core/ResponseModal.js +195 -72
  126. package/dist/cjs/core/ResponseModal.js.map +1 -1
  127. package/dist/cjs/core/ResponsePipeline.d.ts +50 -12
  128. package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
  129. package/dist/cjs/core/ResponsePipeline.js +301 -158
  130. package/dist/cjs/core/ResponsePipeline.js.map +1 -1
  131. package/dist/cjs/core/SessionFinalizer.d.ts +4 -4
  132. package/dist/cjs/core/SessionFinalizer.d.ts.map +1 -1
  133. package/dist/cjs/core/SessionFinalizer.js +36 -9
  134. package/dist/cjs/core/SessionFinalizer.js.map +1 -1
  135. package/dist/cjs/core/SessionManager.d.ts +15 -6
  136. package/dist/cjs/core/SessionManager.d.ts.map +1 -1
  137. package/dist/cjs/core/SessionManager.js +49 -22
  138. package/dist/cjs/core/SessionManager.js.map +1 -1
  139. package/dist/cjs/core/SignalCoordinator.d.ts +5 -5
  140. package/dist/cjs/core/SignalCoordinator.d.ts.map +1 -1
  141. package/dist/cjs/core/SignalCoordinator.js +12 -12
  142. package/dist/cjs/core/SignalCoordinator.js.map +1 -1
  143. package/dist/cjs/core/SignalEvaluator.d.ts +4 -4
  144. package/dist/cjs/core/SignalEvaluator.d.ts.map +1 -1
  145. package/dist/cjs/core/SignalEvaluator.js +6 -6
  146. package/dist/cjs/core/SignalEvaluator.js.map +1 -1
  147. package/dist/cjs/core/SignalProcessor.d.ts +6 -6
  148. package/dist/cjs/core/SignalProcessor.d.ts.map +1 -1
  149. package/dist/cjs/core/SignalProcessor.js +17 -89
  150. package/dist/cjs/core/SignalProcessor.js.map +1 -1
  151. package/dist/cjs/core/Step.d.ts +5 -5
  152. package/dist/cjs/core/Step.d.ts.map +1 -1
  153. package/dist/cjs/core/Step.js +65 -17
  154. package/dist/cjs/core/Step.js.map +1 -1
  155. package/dist/cjs/core/StepLifecycle.d.ts +18 -8
  156. package/dist/cjs/core/StepLifecycle.d.ts.map +1 -1
  157. package/dist/cjs/core/StepLifecycle.js +103 -20
  158. package/dist/cjs/core/StepLifecycle.js.map +1 -1
  159. package/dist/cjs/core/StreamingToolExecutor.d.ts +1 -1
  160. package/dist/cjs/core/StreamingToolExecutor.d.ts.map +1 -1
  161. package/dist/cjs/core/StreamingToolExecutor.js +30 -6
  162. package/dist/cjs/core/StreamingToolExecutor.js.map +1 -1
  163. package/dist/cjs/core/ToolLoopExecutor.d.ts +8 -4
  164. package/dist/cjs/core/ToolLoopExecutor.d.ts.map +1 -1
  165. package/dist/cjs/core/ToolLoopExecutor.js +189 -97
  166. package/dist/cjs/core/ToolLoopExecutor.js.map +1 -1
  167. package/dist/cjs/core/ToolManager.d.ts +6 -6
  168. package/dist/cjs/core/ToolManager.d.ts.map +1 -1
  169. package/dist/cjs/core/ToolManager.js +102 -79
  170. package/dist/cjs/core/ToolManager.js.map +1 -1
  171. package/dist/cjs/core/createAgent.d.ts +2 -2
  172. package/dist/cjs/core/createAgent.d.ts.map +1 -1
  173. package/dist/cjs/core/createAgent.js +2 -2
  174. package/dist/cjs/core/createAgent.js.map +1 -1
  175. package/dist/cjs/core/flow-namespace.d.ts +16 -1
  176. package/dist/cjs/core/flow-namespace.d.ts.map +1 -1
  177. package/dist/cjs/core/flow-namespace.js +26 -4
  178. package/dist/cjs/core/flow-namespace.js.map +1 -1
  179. package/dist/cjs/core/toolGates.d.ts +1 -1
  180. package/dist/cjs/core/toolGates.d.ts.map +1 -1
  181. package/dist/cjs/core/toolGates.js +3 -3
  182. package/dist/cjs/core/toolGates.js.map +1 -1
  183. package/dist/cjs/index.d.ts +50 -45
  184. package/dist/cjs/index.d.ts.map +1 -1
  185. package/dist/cjs/index.js +96 -88
  186. package/dist/cjs/index.js.map +1 -1
  187. package/dist/cjs/providers/AnthropicProvider.d.ts +25 -35
  188. package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
  189. package/dist/cjs/providers/AnthropicProvider.js +30 -426
  190. package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
  191. package/dist/cjs/providers/DeepSeekProvider.d.ts +13 -34
  192. package/dist/cjs/providers/DeepSeekProvider.d.ts.map +1 -1
  193. package/dist/cjs/providers/DeepSeekProvider.js +17 -51
  194. package/dist/cjs/providers/DeepSeekProvider.js.map +1 -1
  195. package/dist/cjs/providers/GeminiProvider.d.ts +22 -75
  196. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  197. package/dist/cjs/providers/GeminiProvider.js +32 -558
  198. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  199. package/dist/cjs/providers/GenericOpenAICompatibleProvider.d.ts +9 -10
  200. package/dist/cjs/providers/GenericOpenAICompatibleProvider.d.ts.map +1 -1
  201. package/dist/cjs/providers/GenericOpenAICompatibleProvider.js +14 -20
  202. package/dist/cjs/providers/GenericOpenAICompatibleProvider.js.map +1 -1
  203. package/dist/cjs/providers/OpenAICompatibleProvider.d.ts +26 -135
  204. package/dist/cjs/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  205. package/dist/cjs/providers/OpenAICompatibleProvider.js +40 -496
  206. package/dist/cjs/providers/OpenAICompatibleProvider.js.map +1 -1
  207. package/dist/cjs/providers/OpenAIProvider.d.ts +14 -20
  208. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
  209. package/dist/cjs/providers/OpenAIProvider.js +21 -28
  210. package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
  211. package/dist/cjs/providers/OpenRouterProvider.d.ts +20 -24
  212. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  213. package/dist/cjs/providers/OpenRouterProvider.js +22 -38
  214. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  215. package/dist/cjs/providers/ProviderAdapter.d.ts +94 -0
  216. package/dist/cjs/providers/ProviderAdapter.d.ts.map +1 -0
  217. package/dist/cjs/providers/ProviderAdapter.js +257 -0
  218. package/dist/cjs/providers/ProviderAdapter.js.map +1 -0
  219. package/dist/cjs/providers/index.d.ts +21 -18
  220. package/dist/cjs/providers/index.d.ts.map +1 -1
  221. package/dist/cjs/providers/index.js +23 -21
  222. package/dist/cjs/providers/index.js.map +1 -1
  223. package/dist/cjs/types/agent.d.ts +37 -11
  224. package/dist/cjs/types/agent.d.ts.map +1 -1
  225. package/dist/cjs/types/ai.d.ts +11 -21
  226. package/dist/cjs/types/ai.d.ts.map +1 -1
  227. package/dist/cjs/types/compaction.d.ts +2 -2
  228. package/dist/cjs/types/compaction.d.ts.map +1 -1
  229. package/dist/cjs/types/errors.d.ts +16 -20
  230. package/dist/cjs/types/errors.d.ts.map +1 -1
  231. package/dist/cjs/types/errors.js +17 -18
  232. package/dist/cjs/types/errors.js.map +1 -1
  233. package/dist/cjs/types/flow.d.ts +56 -39
  234. package/dist/cjs/types/flow.d.ts.map +1 -1
  235. package/dist/cjs/types/history.d.ts +1 -1
  236. package/dist/cjs/types/history.d.ts.map +1 -1
  237. package/dist/cjs/types/index.d.ts +17 -17
  238. package/dist/cjs/types/index.d.ts.map +1 -1
  239. package/dist/cjs/types/index.js +14 -14
  240. package/dist/cjs/types/index.js.map +1 -1
  241. package/dist/cjs/types/persistence.d.ts +2 -2
  242. package/dist/cjs/types/persistence.d.ts.map +1 -1
  243. package/dist/cjs/types/session.d.ts +4 -4
  244. package/dist/cjs/types/session.d.ts.map +1 -1
  245. package/dist/cjs/types/signals.d.ts +3 -3
  246. package/dist/cjs/types/signals.d.ts.map +1 -1
  247. package/dist/cjs/types/template.d.ts +2 -2
  248. package/dist/cjs/types/template.d.ts.map +1 -1
  249. package/dist/cjs/types/tool.d.ts +4 -2
  250. package/dist/cjs/types/tool.d.ts.map +1 -1
  251. package/dist/cjs/types/tool.js.map +1 -1
  252. package/dist/cjs/utils/condition.d.ts +1 -1
  253. package/dist/cjs/utils/condition.d.ts.map +1 -1
  254. package/dist/cjs/utils/condition.js +6 -6
  255. package/dist/cjs/utils/condition.js.map +1 -1
  256. package/dist/cjs/utils/event.d.ts +1 -1
  257. package/dist/cjs/utils/event.d.ts.map +1 -1
  258. package/dist/cjs/utils/event.js +2 -2
  259. package/dist/cjs/utils/event.js.map +1 -1
  260. package/dist/cjs/utils/history.d.ts +2 -2
  261. package/dist/cjs/utils/history.d.ts.map +1 -1
  262. package/dist/cjs/utils/history.js +15 -15
  263. package/dist/cjs/utils/history.js.map +1 -1
  264. package/dist/cjs/utils/index.d.ts +11 -13
  265. package/dist/cjs/utils/index.d.ts.map +1 -1
  266. package/dist/cjs/utils/index.js +57 -58
  267. package/dist/cjs/utils/index.js.map +1 -1
  268. package/dist/cjs/utils/serialize.d.ts +17 -0
  269. package/dist/cjs/utils/serialize.d.ts.map +1 -1
  270. package/dist/cjs/utils/serialize.js +33 -0
  271. package/dist/cjs/utils/serialize.js.map +1 -1
  272. package/dist/cjs/utils/session.d.ts +23 -3
  273. package/dist/cjs/utils/session.d.ts.map +1 -1
  274. package/dist/cjs/utils/session.js +40 -8
  275. package/dist/cjs/utils/session.js.map +1 -1
  276. package/dist/cjs/utils/template.d.ts +3 -3
  277. package/dist/cjs/utils/template.d.ts.map +1 -1
  278. package/dist/cjs/utils/template.js +5 -5
  279. package/dist/cjs/utils/template.js.map +1 -1
  280. package/dist/constants/index.d.ts +1 -0
  281. package/dist/constants/index.js +1 -1
  282. package/dist/core/Agent.d.ts +19 -11
  283. package/dist/core/Agent.d.ts.map +1 -1
  284. package/dist/core/Agent.js +53 -15
  285. package/dist/core/Agent.js.map +1 -1
  286. package/dist/core/AutoChainExecutor.d.ts +12 -22
  287. package/dist/core/AutoChainExecutor.d.ts.map +1 -1
  288. package/dist/core/AutoChainExecutor.js +27 -30
  289. package/dist/core/AutoChainExecutor.js.map +1 -1
  290. package/dist/core/BranchEvaluator.d.ts +4 -4
  291. package/dist/core/BranchEvaluator.d.ts.map +1 -1
  292. package/dist/core/BranchEvaluator.js +2 -2
  293. package/dist/core/BranchEvaluator.js.map +1 -1
  294. package/dist/core/CompactionEngine.d.ts +16 -3
  295. package/dist/core/CompactionEngine.d.ts.map +1 -1
  296. package/dist/core/CompactionEngine.js +30 -6
  297. package/dist/core/CompactionEngine.js.map +1 -1
  298. package/dist/core/DirectiveChainTracker.d.ts +1 -1
  299. package/dist/core/DirectiveChainTracker.d.ts.map +1 -1
  300. package/dist/core/DirectiveChainTracker.js +2 -2
  301. package/dist/core/DirectiveChainTracker.js.map +1 -1
  302. package/dist/core/Events.d.ts +1 -1
  303. package/dist/core/Events.d.ts.map +1 -1
  304. package/dist/core/Events.js +1 -1
  305. package/dist/core/Events.js.map +1 -1
  306. package/dist/core/Flow.d.ts +4 -4
  307. package/dist/core/Flow.d.ts.map +1 -1
  308. package/dist/core/Flow.js +3 -3
  309. package/dist/core/Flow.js.map +1 -1
  310. package/dist/core/FlowRouter.d.ts +4 -4
  311. package/dist/core/FlowRouter.d.ts.map +1 -1
  312. package/dist/core/FlowRouter.js +36 -14
  313. package/dist/core/FlowRouter.js.map +1 -1
  314. package/dist/core/PersistenceManager.d.ts +1 -1
  315. package/dist/core/PersistenceManager.d.ts.map +1 -1
  316. package/dist/core/PersistenceManager.js +3 -3
  317. package/dist/core/PersistenceManager.js.map +1 -1
  318. package/dist/core/PromptComposer.d.ts +4 -4
  319. package/dist/core/PromptComposer.d.ts.map +1 -1
  320. package/dist/core/PromptComposer.js +3 -3
  321. package/dist/core/PromptComposer.js.map +1 -1
  322. package/dist/core/PromptSectionCache.d.ts +2 -2
  323. package/dist/core/PromptSectionCache.d.ts.map +1 -1
  324. package/dist/core/ResponseEngine.d.ts +4 -4
  325. package/dist/core/ResponseEngine.d.ts.map +1 -1
  326. package/dist/core/ResponseEngine.js +2 -2
  327. package/dist/core/ResponseEngine.js.map +1 -1
  328. package/dist/core/ResponseGenerationError.d.ts.map +1 -1
  329. package/dist/core/ResponseGenerationError.js +3 -5
  330. package/dist/core/ResponseGenerationError.js.map +1 -1
  331. package/dist/core/ResponseModal.d.ts +39 -10
  332. package/dist/core/ResponseModal.d.ts.map +1 -1
  333. package/dist/core/ResponseModal.js +152 -29
  334. package/dist/core/ResponseModal.js.map +1 -1
  335. package/dist/core/ResponsePipeline.d.ts +50 -12
  336. package/dist/core/ResponsePipeline.d.ts.map +1 -1
  337. package/dist/core/ResponsePipeline.js +232 -89
  338. package/dist/core/ResponsePipeline.js.map +1 -1
  339. package/dist/core/SessionFinalizer.d.ts +4 -4
  340. package/dist/core/SessionFinalizer.d.ts.map +1 -1
  341. package/dist/core/SessionFinalizer.js +32 -5
  342. package/dist/core/SessionFinalizer.js.map +1 -1
  343. package/dist/core/SessionManager.d.ts +15 -6
  344. package/dist/core/SessionManager.d.ts.map +1 -1
  345. package/dist/core/SessionManager.js +46 -19
  346. package/dist/core/SessionManager.js.map +1 -1
  347. package/dist/core/SignalCoordinator.d.ts +5 -5
  348. package/dist/core/SignalCoordinator.d.ts.map +1 -1
  349. package/dist/core/SignalCoordinator.js +2 -2
  350. package/dist/core/SignalCoordinator.js.map +1 -1
  351. package/dist/core/SignalEvaluator.d.ts +4 -4
  352. package/dist/core/SignalEvaluator.d.ts.map +1 -1
  353. package/dist/core/SignalEvaluator.js +2 -2
  354. package/dist/core/SignalEvaluator.js.map +1 -1
  355. package/dist/core/SignalProcessor.d.ts +6 -6
  356. package/dist/core/SignalProcessor.d.ts.map +1 -1
  357. package/dist/core/SignalProcessor.js +7 -79
  358. package/dist/core/SignalProcessor.js.map +1 -1
  359. package/dist/core/Step.d.ts +5 -5
  360. package/dist/core/Step.d.ts.map +1 -1
  361. package/dist/core/Step.js +53 -5
  362. package/dist/core/Step.js.map +1 -1
  363. package/dist/core/StepLifecycle.d.ts +18 -8
  364. package/dist/core/StepLifecycle.d.ts.map +1 -1
  365. package/dist/core/StepLifecycle.js +98 -15
  366. package/dist/core/StepLifecycle.js.map +1 -1
  367. package/dist/core/StreamingToolExecutor.d.ts +1 -1
  368. package/dist/core/StreamingToolExecutor.d.ts.map +1 -1
  369. package/dist/core/StreamingToolExecutor.js +29 -5
  370. package/dist/core/StreamingToolExecutor.js.map +1 -1
  371. package/dist/core/ToolLoopExecutor.d.ts +8 -4
  372. package/dist/core/ToolLoopExecutor.d.ts.map +1 -1
  373. package/dist/core/ToolLoopExecutor.js +157 -65
  374. package/dist/core/ToolLoopExecutor.js.map +1 -1
  375. package/dist/core/ToolManager.d.ts +6 -6
  376. package/dist/core/ToolManager.d.ts.map +1 -1
  377. package/dist/core/ToolManager.js +44 -21
  378. package/dist/core/ToolManager.js.map +1 -1
  379. package/dist/core/createAgent.d.ts +2 -2
  380. package/dist/core/createAgent.d.ts.map +1 -1
  381. package/dist/core/createAgent.js +1 -1
  382. package/dist/core/createAgent.js.map +1 -1
  383. package/dist/core/flow-namespace.d.ts +16 -1
  384. package/dist/core/flow-namespace.d.ts.map +1 -1
  385. package/dist/core/flow-namespace.js +23 -1
  386. package/dist/core/flow-namespace.js.map +1 -1
  387. package/dist/core/toolGates.d.ts +1 -1
  388. package/dist/core/toolGates.d.ts.map +1 -1
  389. package/dist/core/toolGates.js +1 -1
  390. package/dist/core/toolGates.js.map +1 -1
  391. package/dist/index.d.ts +50 -45
  392. package/dist/index.d.ts.map +1 -1
  393. package/dist/index.js +34 -30
  394. package/dist/index.js.map +1 -1
  395. package/dist/providers/AnthropicProvider.d.ts +25 -35
  396. package/dist/providers/AnthropicProvider.d.ts.map +1 -1
  397. package/dist/providers/AnthropicProvider.js +30 -423
  398. package/dist/providers/AnthropicProvider.js.map +1 -1
  399. package/dist/providers/DeepSeekProvider.d.ts +13 -34
  400. package/dist/providers/DeepSeekProvider.d.ts.map +1 -1
  401. package/dist/providers/DeepSeekProvider.js +16 -47
  402. package/dist/providers/DeepSeekProvider.js.map +1 -1
  403. package/dist/providers/GeminiProvider.d.ts +22 -75
  404. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  405. package/dist/providers/GeminiProvider.js +32 -558
  406. package/dist/providers/GeminiProvider.js.map +1 -1
  407. package/dist/providers/GenericOpenAICompatibleProvider.d.ts +9 -10
  408. package/dist/providers/GenericOpenAICompatibleProvider.d.ts.map +1 -1
  409. package/dist/providers/GenericOpenAICompatibleProvider.js +13 -16
  410. package/dist/providers/GenericOpenAICompatibleProvider.js.map +1 -1
  411. package/dist/providers/OpenAICompatibleProvider.d.ts +26 -135
  412. package/dist/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  413. package/dist/providers/OpenAICompatibleProvider.js +39 -495
  414. package/dist/providers/OpenAICompatibleProvider.js.map +1 -1
  415. package/dist/providers/OpenAIProvider.d.ts +14 -20
  416. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  417. package/dist/providers/OpenAIProvider.js +20 -24
  418. package/dist/providers/OpenAIProvider.js.map +1 -1
  419. package/dist/providers/OpenRouterProvider.d.ts +20 -24
  420. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  421. package/dist/providers/OpenRouterProvider.js +21 -34
  422. package/dist/providers/OpenRouterProvider.js.map +1 -1
  423. package/dist/providers/ProviderAdapter.d.ts +94 -0
  424. package/dist/providers/ProviderAdapter.d.ts.map +1 -0
  425. package/dist/providers/ProviderAdapter.js +251 -0
  426. package/dist/providers/ProviderAdapter.js.map +1 -0
  427. package/dist/providers/index.d.ts +21 -18
  428. package/dist/providers/index.d.ts.map +1 -1
  429. package/dist/providers/index.js +13 -10
  430. package/dist/providers/index.js.map +1 -1
  431. package/dist/types/agent.d.ts +37 -11
  432. package/dist/types/agent.d.ts.map +1 -1
  433. package/dist/types/ai.d.ts +11 -21
  434. package/dist/types/ai.d.ts.map +1 -1
  435. package/dist/types/compaction.d.ts +2 -2
  436. package/dist/types/compaction.d.ts.map +1 -1
  437. package/dist/types/errors.d.ts +16 -20
  438. package/dist/types/errors.d.ts.map +1 -1
  439. package/dist/types/errors.js +15 -16
  440. package/dist/types/errors.js.map +1 -1
  441. package/dist/types/flow.d.ts +56 -39
  442. package/dist/types/flow.d.ts.map +1 -1
  443. package/dist/types/history.d.ts +1 -1
  444. package/dist/types/history.d.ts.map +1 -1
  445. package/dist/types/index.d.ts +17 -17
  446. package/dist/types/index.d.ts.map +1 -1
  447. package/dist/types/index.js +4 -4
  448. package/dist/types/index.js.map +1 -1
  449. package/dist/types/persistence.d.ts +2 -2
  450. package/dist/types/persistence.d.ts.map +1 -1
  451. package/dist/types/session.d.ts +4 -4
  452. package/dist/types/session.d.ts.map +1 -1
  453. package/dist/types/signals.d.ts +3 -3
  454. package/dist/types/signals.d.ts.map +1 -1
  455. package/dist/types/template.d.ts +2 -2
  456. package/dist/types/template.d.ts.map +1 -1
  457. package/dist/types/tool.d.ts +4 -2
  458. package/dist/types/tool.d.ts.map +1 -1
  459. package/dist/types/tool.js.map +1 -1
  460. package/dist/utils/condition.d.ts +1 -1
  461. package/dist/utils/condition.d.ts.map +1 -1
  462. package/dist/utils/condition.js +2 -2
  463. package/dist/utils/condition.js.map +1 -1
  464. package/dist/utils/event.d.ts +1 -1
  465. package/dist/utils/event.d.ts.map +1 -1
  466. package/dist/utils/event.js +1 -1
  467. package/dist/utils/event.js.map +1 -1
  468. package/dist/utils/history.d.ts +2 -2
  469. package/dist/utils/history.d.ts.map +1 -1
  470. package/dist/utils/history.js +1 -1
  471. package/dist/utils/history.js.map +1 -1
  472. package/dist/utils/index.d.ts +11 -13
  473. package/dist/utils/index.d.ts.map +1 -1
  474. package/dist/utils/index.js +11 -12
  475. package/dist/utils/index.js.map +1 -1
  476. package/dist/utils/serialize.d.ts +17 -0
  477. package/dist/utils/serialize.d.ts.map +1 -1
  478. package/dist/utils/serialize.js +31 -0
  479. package/dist/utils/serialize.js.map +1 -1
  480. package/dist/utils/session.d.ts +23 -3
  481. package/dist/utils/session.d.ts.map +1 -1
  482. package/dist/utils/session.js +35 -6
  483. package/dist/utils/session.js.map +1 -1
  484. package/dist/utils/template.d.ts +3 -3
  485. package/dist/utils/template.d.ts.map +1 -1
  486. package/dist/utils/template.js +1 -1
  487. package/dist/utils/template.js.map +1 -1
  488. package/docs/concepts/architecture.md +3 -3
  489. package/docs/concepts/directives.md +1 -1
  490. package/docs/guides/error-handling.md +46 -45
  491. package/docs/guides/flow-control.md +8 -1
  492. package/docs/guides/instructions.md +15 -6
  493. package/docs/guides/persistence.md +12 -5
  494. package/docs/guides/streaming.md +10 -0
  495. package/docs/migration/README.md +4 -0
  496. package/docs/migration/v2-3-to-v2-4.md +4 -0
  497. package/docs/migration/v2-6-to-v2-7.md +246 -0
  498. package/docs/reference/adapters.md +15 -1
  499. package/docs/reference/branches.md +2 -0
  500. package/docs/reference/create-agent.md +28 -0
  501. package/docs/reference/directive.md +1 -1
  502. package/docs/reference/errors.md +30 -32
  503. package/docs/reference/providers.md +63 -60
  504. package/docs/reference/step.md +28 -21
  505. package/docs/reference/tool.md +14 -5
  506. package/docs/start/02-first-agent.md +8 -4
  507. package/docs/start/03-collect-data.md +19 -10
  508. package/examples/01-quickstart.ts +2 -2
  509. package/examples/02-data-extraction.ts +1 -1
  510. package/examples/03-tools.ts +1 -1
  511. package/examples/04-instructions.ts +2 -2
  512. package/examples/05-branching.ts +3 -3
  513. package/examples/06-flow-control.ts +3 -3
  514. package/examples/07-streaming.ts +1 -1
  515. package/examples/08-persistence.ts +2 -2
  516. package/examples/09-signals.ts +1 -1
  517. package/examples/tsconfig.json +6 -3
  518. package/package.json +9 -7
  519. package/src/adapters/MemoryAdapter.ts +33 -37
  520. package/src/adapters/MongoAdapter.ts +3 -3
  521. package/src/adapters/OpenSearchAdapter.ts +4 -4
  522. package/src/adapters/PostgreSQLAdapter.ts +26 -21
  523. package/src/adapters/PrismaAdapter.ts +4 -4
  524. package/src/adapters/RedisAdapter.ts +84 -37
  525. package/src/adapters/SQLiteAdapter.ts +13 -34
  526. package/src/adapters/index.ts +13 -13
  527. package/src/adapters/sessionRow.ts +57 -0
  528. package/src/core/Agent.ts +64 -18
  529. package/src/core/AutoChainExecutor.ts +44 -57
  530. package/src/core/BranchEvaluator.ts +5 -5
  531. package/src/core/CompactionEngine.ts +42 -8
  532. package/src/core/DirectiveChainTracker.ts +3 -3
  533. package/src/core/Events.ts +2 -2
  534. package/src/core/Flow.ts +9 -9
  535. package/src/core/FlowRouter.ts +46 -20
  536. package/src/core/PersistenceManager.ts +4 -4
  537. package/src/core/PromptComposer.ts +8 -8
  538. package/src/core/PromptSectionCache.ts +2 -2
  539. package/src/core/ResponseEngine.ts +6 -6
  540. package/src/core/ResponseGenerationError.ts +3 -6
  541. package/src/core/ResponseModal.ts +205 -39
  542. package/src/core/ResponsePipeline.ts +301 -107
  543. package/src/core/SessionFinalizer.ts +40 -10
  544. package/src/core/SessionManager.ts +61 -27
  545. package/src/core/SignalCoordinator.ts +7 -7
  546. package/src/core/SignalEvaluator.ts +6 -6
  547. package/src/core/SignalProcessor.ts +13 -93
  548. package/src/core/Step.ts +87 -10
  549. package/src/core/StepLifecycle.ts +129 -26
  550. package/src/core/StreamingToolExecutor.ts +33 -8
  551. package/src/core/ToolLoopExecutor.ts +206 -80
  552. package/src/core/ToolManager.ts +53 -26
  553. package/src/core/createAgent.ts +2 -2
  554. package/src/core/flow-namespace.ts +31 -2
  555. package/src/core/toolGates.ts +2 -2
  556. package/src/index.ts +59 -45
  557. package/src/providers/AnthropicProvider.ts +44 -595
  558. package/src/providers/DeepSeekProvider.ts +27 -92
  559. package/src/providers/GeminiProvider.ts +41 -752
  560. package/src/providers/GenericOpenAICompatibleProvider.ts +19 -27
  561. package/src/providers/OpenAICompatibleProvider.ts +58 -764
  562. package/src/providers/OpenAIProvider.ts +31 -55
  563. package/src/providers/OpenRouterProvider.ts +40 -70
  564. package/src/providers/ProviderAdapter.ts +368 -0
  565. package/src/providers/index.ts +21 -23
  566. package/src/types/agent.ts +36 -11
  567. package/src/types/ai.ts +11 -21
  568. package/src/types/compaction.ts +2 -2
  569. package/src/types/errors.ts +17 -35
  570. package/src/types/flow.ts +44 -46
  571. package/src/types/history.ts +1 -1
  572. package/src/types/index.ts +18 -17
  573. package/src/types/persistence.ts +2 -2
  574. package/src/types/session.ts +4 -4
  575. package/src/types/signals.ts +3 -3
  576. package/src/types/template.ts +2 -2
  577. package/src/types/tool.ts +4 -2
  578. package/src/utils/condition.ts +3 -3
  579. package/src/utils/event.ts +1 -1
  580. package/src/utils/history.ts +3 -3
  581. package/src/utils/index.ts +14 -15
  582. package/src/utils/serialize.ts +38 -0
  583. package/src/utils/session.ts +46 -9
  584. package/src/utils/template.ts +3 -3
  585. package/dist/cjs/core/DirectiveBus.d.ts +0 -88
  586. package/dist/cjs/core/DirectiveBus.d.ts.map +0 -1
  587. package/dist/cjs/core/DirectiveBus.js +0 -196
  588. package/dist/cjs/core/DirectiveBus.js.map +0 -1
  589. package/dist/cjs/providers/errorClassification.d.ts +0 -61
  590. package/dist/cjs/providers/errorClassification.d.ts.map +0 -1
  591. package/dist/cjs/providers/errorClassification.js +0 -123
  592. package/dist/cjs/providers/errorClassification.js.map +0 -1
  593. package/dist/cjs/utils/retry.d.ts +0 -67
  594. package/dist/cjs/utils/retry.d.ts.map +0 -1
  595. package/dist/cjs/utils/retry.js +0 -201
  596. package/dist/cjs/utils/retry.js.map +0 -1
  597. package/dist/core/DirectiveBus.d.ts +0 -88
  598. package/dist/core/DirectiveBus.d.ts.map +0 -1
  599. package/dist/core/DirectiveBus.js +0 -192
  600. package/dist/core/DirectiveBus.js.map +0 -1
  601. package/dist/providers/errorClassification.d.ts +0 -61
  602. package/dist/providers/errorClassification.d.ts.map +0 -1
  603. package/dist/providers/errorClassification.js +0 -116
  604. package/dist/providers/errorClassification.js.map +0 -1
  605. package/dist/utils/retry.d.ts +0 -67
  606. package/dist/utils/retry.d.ts.map +0 -1
  607. package/dist/utils/retry.js +0 -193
  608. package/dist/utils/retry.js.map +0 -1
  609. package/src/core/DirectiveBus.ts +0 -248
  610. package/src/providers/errorClassification.ts +0 -172
  611. package/src/utils/retry.ts +0 -274
@@ -2,14 +2,14 @@
2
2
  * Response processing utilities shared between respond() and respondStream() methods
3
3
  */
4
4
 
5
- import type {
5
+ import type { EndedFlow,
6
6
  AgentOptions,
7
7
  Event,
8
8
  SessionState,
9
9
  Directive,
10
10
  StructuredSchema,
11
- } from "../types";
12
- import type { SignalFiring } from "../types/signals";
11
+ } from "../types/index.js";
12
+ import type { SignalFiring } from "../types/signals.js";
13
13
  import {
14
14
  createSession,
15
15
  enterStep,
@@ -18,16 +18,16 @@ import {
18
18
  historyToEvents,
19
19
  eventsToHistory,
20
20
  getLastMessageFromHistory,
21
- } from "../utils";
22
- import { enterFlow } from "../utils/session";
23
- import { createTemplateContext } from "../utils/template";
24
- import { Flow } from "./Flow";
25
- import { Step, FlowConfigurationError } from "../core/Step";
26
- import { FlowRouter } from "./FlowRouter";
27
- import { evaluateBranches, createAiConditionEvaluator } from "./BranchEvaluator";
28
- import { DirectiveChainTracker } from "./DirectiveChainTracker";
29
- import { ResponseGenerationError } from "./ResponseGenerationError";
30
- import type { SignalCoordinator } from "./SignalCoordinator";
21
+ } from "../utils/index.js";
22
+ import { enterFlow } from "../utils/session.js";
23
+ import { createTemplateContext } from "../utils/template.js";
24
+ import { Flow } from "./Flow.js";
25
+ import { Step, FlowConfigurationError } from "../core/Step.js";
26
+ import { FlowRouter } from "./FlowRouter.js";
27
+ import { evaluateBranches, createAiConditionEvaluator } from "./BranchEvaluator.js";
28
+ import { DirectiveChainTracker } from "./DirectiveChainTracker.js";
29
+ import { ResponseGenerationError } from "./ResponseGenerationError.js";
30
+ import type { SignalCoordinator } from "./SignalCoordinator.js";
31
31
 
32
32
  /**
33
33
  * Position fields on a Directive that represent a navigation decision.
@@ -51,6 +51,27 @@ export function hasDirectivePositionField<TContext = unknown, TData = unknown>(
51
51
  );
52
52
  }
53
53
 
54
+ /**
55
+ * Compute the per-key data writes the signal phase applied to a session.
56
+ * The signal phase and routing start from the same input session, so any
57
+ * key whose value differs between the input and the signal output was
58
+ * written by the signal phase alone (handler `updateData` calls).
59
+ */
60
+ function diffSignalDataWrites<TData>(
61
+ before: Partial<TData> | undefined,
62
+ after: Partial<TData> | undefined,
63
+ ): Partial<TData> {
64
+ const beforeData = (before ?? {}) as Record<string, unknown>;
65
+ const afterData = (after ?? {}) as Record<string, unknown>;
66
+ const writes: Record<string, unknown> = {};
67
+ for (const key of Object.keys(afterData)) {
68
+ if (afterData[key] !== beforeData[key]) {
69
+ writes[key] = afterData[key];
70
+ }
71
+ }
72
+ return writes as Partial<TData>;
73
+ }
74
+
54
75
  export interface ResponsePreparationResult<TContext, TData = unknown> {
55
76
  effectiveContext: TContext;
56
77
  session: SessionState<TData>;
@@ -65,6 +86,8 @@ export interface RoutingResult<TContext, TData = unknown> {
65
86
  session: SessionState<TData>;
66
87
  isFlowComplete: boolean;
67
88
  completedFlows?: Flow<TContext, TData>[];
89
+ /** Flows exited via pendingDirective redirects/resets this turn. */
90
+ endedFlows?: EndedFlow[];
68
91
  }
69
92
 
70
93
  /**
@@ -154,8 +177,10 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
154
177
  history: Event[];
155
178
  context: TContext;
156
179
  signal?: AbortSignal;
180
+ /** Restrict ROUTING candidates; directive targets still use the full registry. */
181
+ allowedFlows?: string[];
157
182
  }): Promise<RoutingResult<TContext, TData>> {
158
- const { session, history, context, signal } = params;
183
+ const { session, history, context, signal, allowedFlows } = params;
159
184
 
160
185
  // PHASE 2: ROUTING + STEP SELECTION - Determine which flow and step to use (combined)
161
186
  let selectedFlow: Flow<TContext, TData> | undefined;
@@ -164,9 +189,23 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
164
189
  let isFlowComplete = false;
165
190
  let completedFlows: Flow<TContext, TData>[] = [];
166
191
  let targetSession = session;
192
+ // Flows exited this turn via pendingDirective redirect/reset
193
+ const endedFlows: EndedFlow[] = [];
194
+ let exitPrevFlowId: string | undefined;
195
+ let exitPrevFlowTitle: string | undefined;
196
+ let exitReason: EndedFlow['reason'] | undefined;
167
197
 
168
198
  // Get flows early since we need them for pending directives
169
199
  const flows = this.getFlows();
200
+ // Routing candidates may be narrowed for this turn (entry pins); the
201
+ // pendingDirective applier below still resolves targets against the FULL
202
+ // registry so declared redirects always resolve.
203
+ const routingFlows =
204
+ allowedFlows && allowedFlows.length > 0
205
+ ? flows.filter(
206
+ (f) => allowedFlows.includes(f.id) || allowedFlows.includes(f.title),
207
+ )
208
+ : flows;
170
209
 
171
210
  // Check for pending directive from previous flow completion or external dispatch
172
211
  if (targetSession.pendingDirective) {
@@ -195,8 +234,16 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
195
234
  pendingDirective: nextDirective,
196
235
  };
197
236
 
237
+ // Capture the flow we're leaving so directive-driven exits surface on
238
+ // the response (endedFlows).
239
+ exitPrevFlowId = targetSession.currentFlow?.id;
240
+ exitPrevFlowTitle = exitPrevFlowId
241
+ ? flows.find((f) => f.id === exitPrevFlowId)?.title
242
+ : undefined;
243
+
198
244
  // Apply the directive: resolve position field to a flow/step
199
245
  if (directive.goTo) {
246
+ exitReason = 'goto';
200
247
  const flowTarget = typeof directive.goTo === 'string'
201
248
  ? directive.goTo
202
249
  : directive.goTo.flow;
@@ -283,6 +330,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
283
330
  }
284
331
  }
285
332
  } else if (directive.reset) {
333
+ exitReason = 'reset';
286
334
  // Reset current flow
287
335
  if (targetSession.currentFlow) {
288
336
  const currentFlow = flows.find(r => r.id === targetSession.currentFlow?.id);
@@ -316,10 +364,19 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
316
364
  // Skip FlowRouter.decideFlowAndStep — the directive resolved the position
317
365
  }
318
366
 
319
- // If no pending transition or transition handled, do normal routing
320
- if (flows.length > 0 && !selectedFlow) {
367
+ if (
368
+ exitReason &&
369
+ exitPrevFlowId &&
370
+ (!selectedFlow || selectedFlow.id !== exitPrevFlowId)
371
+ ) {
372
+ endedFlows.push({ flowId: exitPrevFlowId, title: exitPrevFlowTitle, reason: exitReason });
373
+ }
374
+
375
+ // If no pending transition or transition handled, do normal routing.
376
+ // Candidates honor allowedFlows; completion scoring runs over the same set.
377
+ if (routingFlows.length > 0 && !selectedFlow) {
321
378
  const orchestration = await this.flowRouter.decideFlowAndStep({
322
- flows: flows,
379
+ flows: routingFlows,
323
380
  session: targetSession,
324
381
  history,
325
382
  agentOptions: this.options,
@@ -350,9 +407,41 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
350
407
  session: targetSession,
351
408
  isFlowComplete,
352
409
  completedFlows,
410
+ ...(endedFlows.length > 0 ? { endedFlows } : {}),
353
411
  };
354
412
  }
355
413
 
414
+ /**
415
+ * Combine a routing-derived session with the signal phase's non-position
416
+ * mutations. The routing session is the base — it owns position state AND
417
+ * data merges such as flow initialData and directive-carried writes; the
418
+ * signal phase's mutations (trigger recording under `signals`, handler
419
+ * data writes) are re-applied on top. Both phases start from the same
420
+ * input session, so their mutations cannot silently collide.
421
+ */
422
+ private combineRoutingAndSignalSessions(params: {
423
+ inputSession: SessionState<TData>;
424
+ signalSession: SessionState<TData>;
425
+ routingSession: SessionState<TData>;
426
+ }): SessionState<TData> {
427
+ const { inputSession, signalSession, routingSession } = params;
428
+ let combined = routingSession;
429
+
430
+ if (signalSession.signals !== inputSession.signals) {
431
+ combined = { ...combined, signals: signalSession.signals };
432
+ }
433
+
434
+ const signalWrites = diffSignalDataWrites(
435
+ inputSession.data,
436
+ signalSession.data,
437
+ );
438
+ if (Object.keys(signalWrites).length > 0) {
439
+ combined = mergeCollected(combined, signalWrites);
440
+ }
441
+
442
+ return combined;
443
+ }
444
+
356
445
  /**
357
446
  * Determine next step and update session
358
447
  */
@@ -365,11 +454,23 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
365
454
  context: TContext;
366
455
  /** Merged directive from the directive bus (pre-LLM + post-LLM phases). */
367
456
  busDirective?: Directive<TContext, TData>;
368
- }): Promise<{ nextStep: Step<TContext, TData> | undefined; session: SessionState<TData>; flowChanged?: Flow<TContext, TData> }> {
457
+ }): Promise<{
458
+ nextStep: Step<TContext, TData> | undefined;
459
+ session: SessionState<TData>;
460
+ flowChanged?: Flow<TContext, TData>;
461
+ /**
462
+ * The turn's authoritative completion verdict — callers must use this, not
463
+ * the `isFlowComplete` they passed in. The router derives its verdict from
464
+ * the LINEAR chain alone (the current step has no successor), before
465
+ * branches run, so what it returns is only a proposal. Branches win over
466
+ * flow completion (Algorithm 1, STEP 1) and this is where that resolves.
467
+ */
468
+ isFlowComplete: boolean;
469
+ }> {
369
470
  const { selectedFlow, selectedStep, session, isFlowComplete, context, busDirective } = params;
370
471
 
371
472
  if (!selectedFlow) {
372
- return { nextStep: undefined, session };
473
+ return { nextStep: undefined, session, isFlowComplete };
373
474
  }
374
475
 
375
476
  // ─── GUARD: directive bus winner with a position field preempts branches ───
@@ -381,7 +482,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
381
482
  logger.debug(
382
483
  `[ResponsePipeline] Directive bus winner has position field — skipping branch evaluation and linear/AI selection`,
383
484
  );
384
- return { nextStep: undefined, session };
485
+ return { nextStep: undefined, session, isFlowComplete };
385
486
  }
386
487
 
387
488
  // STEP 1 (Algorithm 1): branches win over linear chain AND flow completion.
@@ -397,14 +498,21 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
397
498
  currentStep, selectedFlow, session, context
398
499
  );
399
500
  if (branchResult) {
400
- return branchResult;
501
+ // A branch that resolved a position overrides the router's completion
502
+ // proposal. Without this, a branch that parks on a step of its own
503
+ // flow (`then: '<stepId>'`, `then: { reset: true }`) keeps
504
+ // isFlowComplete=true, the caller drops the step it just resolved,
505
+ // and the turn ends as a silent completion — no LLM call, empty
506
+ // reply, flow marked completed for the rest of the session.
507
+ const resolvedPosition = Boolean(branchResult.nextStep || branchResult.flowChanged);
508
+ return { ...branchResult, isFlowComplete: resolvedPosition ? false : isFlowComplete };
401
509
  }
402
510
  // undefined → fall through to linear/AI selection or flow completion
403
511
  }
404
512
  }
405
513
 
406
514
  if (isFlowComplete) {
407
- return { nextStep: undefined, session };
515
+ return { nextStep: undefined, session, isFlowComplete: true };
408
516
  }
409
517
 
410
518
  let nextStep: Step<TContext, TData>;
@@ -457,7 +565,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
457
565
  nextStep = currentStepInstance;
458
566
  }
459
567
  }
460
- return { nextStep, session };
568
+ return { nextStep, session, isFlowComplete: false };
461
569
  }
462
570
  }
463
571
 
@@ -469,7 +577,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
469
577
  );
470
578
  logger.debug(`[ResponsePipeline] Entered step: ${nextStep.id}`);
471
579
 
472
- return { nextStep, session: updatedSession };
580
+ return { nextStep, session: updatedSession, isFlowComplete: false };
473
581
  }
474
582
 
475
583
  /**
@@ -679,6 +787,8 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
679
787
  history: Event[]; // Use Event[] for internal processing
680
788
  context: TContext;
681
789
  signal?: AbortSignal;
790
+ /** Restrict this turn's routing candidates (entry pins). */
791
+ allowedFlows?: string[];
682
792
  }): Promise<{
683
793
  selectedFlow?: Flow<TContext, TData>;
684
794
  selectedStep?: Step<TContext, TData>;
@@ -693,16 +803,21 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
693
803
  signalHalted?: boolean;
694
804
  /** Reply text from the halt directive. */
695
805
  signalHaltReply?: string;
806
+ /** Flows exited via pendingDirective redirects/resets this turn. */
807
+ endedFlows?: EndedFlow[];
696
808
  }> {
697
809
  try {
698
810
  // Create a fresh chain tracker for this turn (Requirement 22.1)
699
811
  this.createChainTracker();
700
812
 
701
813
  // ROUTING SKIP OPTIMIZATION (Requirements 20.1, 20.2, 20.3):
702
- // When the current step has collect fields AND pre-extraction populates at least
703
- // one of those fields, skip FlowRouter.decideFlowAndStep for this turn.
704
- const routingSkipResult = await this.attemptRoutingSkipForCollect(params);
705
- if (routingSkipResult) {
814
+ // When the current step has collect fields AND step-scoped pre-extraction
815
+ // populates at least one of those fields, skip FlowRouter.decideFlowAndStep
816
+ // for this turn. The check's extraction result is reused by the fallthrough
817
+ // paths below so a turn costs at most ONE extraction LLM call.
818
+ const routingSkipCheck = await this.attemptRoutingSkipForCollect(params);
819
+ if (routingSkipCheck.skipResult) {
820
+ const skipResult = routingSkipCheck.skipResult;
706
821
  // Even when routing is skipped, run pre-signal phase if processor is present
707
822
  if (this.signalCoordinator.enabled) {
708
823
  const signalResult = await this.signalCoordinator.runPrePhase(
@@ -711,7 +826,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
711
826
  // If signal halts, override the routing skip result
712
827
  if (signalResult.mergedDirective?.halt) {
713
828
  return {
714
- ...routingSkipResult,
829
+ ...skipResult,
715
830
  session: signalResult.updatedSession,
716
831
  signalFirings: signalResult.firings,
717
832
  signalHalted: true,
@@ -722,15 +837,20 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
722
837
  if (hasDirectivePositionField(signalResult.mergedDirective)) {
723
838
  return this.signalCoordinator.applyPositionDirective(signalResult);
724
839
  }
725
- // Non-position directive: propagate for pre-LLM augmentation
840
+ // Non-position directive: keep the skip result (extraction merges,
841
+ // step entry) and re-apply the signal phase's non-position mutations
726
842
  return {
727
- ...routingSkipResult,
728
- session: signalResult.updatedSession,
843
+ ...skipResult,
844
+ session: this.combineRoutingAndSignalSessions({
845
+ inputSession: params.session,
846
+ signalSession: signalResult.updatedSession,
847
+ routingSession: skipResult.session,
848
+ }),
729
849
  signalFirings: signalResult.firings,
730
850
  signalPreDirective: signalResult.mergedDirective || undefined,
731
851
  };
732
852
  }
733
- return routingSkipResult;
853
+ return skipResult;
734
854
  }
735
855
 
736
856
  // ── PARALLEL PRE-SIGNAL PHASE + ROUTING (Algorithm 5) ────────────────
@@ -747,6 +867,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
747
867
  history: params.history,
748
868
  context: params.context,
749
869
  signal: params.signal,
870
+ allowedFlows: params.allowedFlows,
750
871
  }),
751
872
  ]);
752
873
 
@@ -770,44 +891,45 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
770
891
 
771
892
  // ── Requirement 8.4: non-position directive → use routing, propagate augmentation ──
772
893
  // ── Requirement 8.5: no directive → use routing as-is ─────────────
773
- let updatedSession = signalResult.updatedSession;
894
+ // Base = routed session (position state AND router data merges such as
895
+ // flow initialData survive); the signal phase's non-position mutations
896
+ // (trigger state, handler data writes) are re-applied on top.
897
+ let updatedSession = this.combineRoutingAndSignalSessions({
898
+ inputSession: params.session,
899
+ signalSession: signalResult.updatedSession,
900
+ routingSession: routingResult.session,
901
+ });
774
902
 
775
- // Apply data/context updates from signal to the routed session
903
+ // Explicit dataUpdate from the signal's merged directive lands last
776
904
  if (signalResult.mergedDirective?.dataUpdate) {
777
905
  updatedSession = mergeCollected(updatedSession, signalResult.mergedDirective.dataUpdate);
778
906
  }
779
907
 
780
- // Use routing result for flow/step, but carry signal session state
781
- // Merge routing session changes on top of signal session
782
- const routingSession = routingResult.session;
783
- updatedSession = {
784
- ...updatedSession,
785
- currentFlow: routingSession.currentFlow,
786
- currentStep: routingSession.currentStep,
787
- flowHistory: routingSession.flowHistory,
788
- pendingDirective: routingSession.pendingDirective,
789
- };
790
-
791
908
  const isFlowComplete = routingResult.isFlowComplete;
792
909
 
793
- // PRE-EXTRACTION: same logic as below — extract data from user message
910
+ // PRE-EXTRACTION: same logic as below — extract data from user message.
911
+ // Reuses the routing-skip check's step-scoped extraction when it already
912
+ // ran this turn (at most ONE extraction LLM call per turn).
794
913
  if (routingResult.selectedFlow && !isFlowComplete) {
795
- if (this.shouldPreExtractData(routingResult.selectedFlow)) {
914
+ let extractedData: Partial<TData> | undefined;
915
+ if (routingSkipCheck.extractionRan) {
916
+ extractedData = routingSkipCheck.extractedData;
917
+ } else if (this.shouldPreExtractData(routingResult.selectedFlow)) {
796
918
  logger.debug(
797
919
  `[ResponsePipeline] Pre-extracting data for flow: ${routingResult.selectedFlow.title}`
798
920
  );
799
- const extractedData = await this.preExtractFlowData({
921
+ extractedData = await this.preExtractFlowData({
800
922
  route: routingResult.selectedFlow,
801
923
  history: params.history,
802
924
  context: params.context,
803
925
  session: updatedSession,
804
926
  signal: params.signal,
805
927
  });
806
- if (extractedData && Object.keys(extractedData).length > 0) {
807
- logger.debug(`[ResponsePipeline] Pre-extracted data:`, extractedData);
808
- updatedSession = mergeCollected(updatedSession, extractedData);
809
- await this.updateCollectedData(extractedData);
810
- }
928
+ }
929
+ if (extractedData && Object.keys(extractedData).length > 0) {
930
+ logger.debug(`[ResponsePipeline] Pre-extracted data:`, extractedData);
931
+ updatedSession = mergeCollected(updatedSession, extractedData);
932
+ await this.updateCollectedData(extractedData);
811
933
  }
812
934
  }
813
935
 
@@ -825,9 +947,10 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
825
947
  selectedStep: stepResult.nextStep,
826
948
  responseDirectives: routingResult.responseDirectives,
827
949
  session: stepResult.session,
828
- isFlowComplete: stepResult.flowChanged ? false : isFlowComplete,
950
+ isFlowComplete: stepResult.isFlowComplete,
829
951
  signalFirings: signalResult.firings,
830
952
  signalPreDirective: signalResult.mergedDirective || undefined,
953
+ endedFlows: routingResult.endedFlows,
831
954
  };
832
955
  }
833
956
 
@@ -837,39 +960,45 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
837
960
  history: params.history,
838
961
  context: params.context,
839
962
  signal: params.signal,
963
+ allowedFlows: params.allowedFlows,
840
964
  });
841
965
 
842
966
  let updatedSession = routingResult.session;
843
967
  const isFlowComplete = routingResult.isFlowComplete;
844
968
 
845
969
  // PRE-EXTRACTION: If entering a flow that collects data, extract data from user message first
846
- // This allows us to skip steps whose data is already provided
970
+ // This allows us to skip steps whose data is already provided.
971
+ // Reuses the routing-skip check's step-scoped extraction when it already
972
+ // ran this turn — at most ONE extraction LLM call per turn.
847
973
  if (routingResult.selectedFlow && !isFlowComplete) {
848
974
  // Always pre-extract when flow collects data (not just on new flow entry)
849
975
  // This ensures step selection has the most up-to-date data
850
- if (this.shouldPreExtractData(routingResult.selectedFlow)) {
976
+ let extractedData: Partial<TData> | undefined;
977
+ if (routingSkipCheck.extractionRan) {
978
+ extractedData = routingSkipCheck.extractedData;
979
+ } else if (this.shouldPreExtractData(routingResult.selectedFlow)) {
851
980
  logger.debug(
852
981
  `[ResponsePipeline] Pre-extracting data for flow: ${routingResult.selectedFlow.title}`
853
982
  );
854
983
 
855
- const extractedData = await this.preExtractFlowData({
984
+ extractedData = await this.preExtractFlowData({
856
985
  route: routingResult.selectedFlow,
857
986
  history: params.history,
858
987
  context: params.context,
859
988
  session: updatedSession,
860
989
  signal: params.signal,
861
990
  });
991
+ }
862
992
 
863
- if (extractedData && Object.keys(extractedData).length > 0) {
864
- logger.debug(
865
- `[ResponsePipeline] Pre-extracted data:`,
866
- extractedData
867
- );
868
- // Merge pre-extracted data into session before step selection
869
- updatedSession = mergeCollected(updatedSession, extractedData);
870
- // Also update agent's collected data
871
- await this.updateCollectedData(extractedData);
872
- }
993
+ if (extractedData && Object.keys(extractedData).length > 0) {
994
+ logger.debug(
995
+ `[ResponsePipeline] Pre-extracted data:`,
996
+ extractedData
997
+ );
998
+ // Merge pre-extracted data into session before step selection
999
+ updatedSession = mergeCollected(updatedSession, extractedData);
1000
+ // Also update agent's collected data
1001
+ await this.updateCollectedData(extractedData);
873
1002
  }
874
1003
  }
875
1004
 
@@ -887,8 +1016,10 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
887
1016
  selectedStep: stepResult.nextStep, // Use the determined next step
888
1017
  responseDirectives: routingResult.responseDirectives,
889
1018
  session: stepResult.session,
890
- // If a branch changed the flow, the original isFlowComplete no longer applies
891
- isFlowComplete: stepResult.flowChanged ? false : isFlowComplete,
1019
+ // determineNextStep owns the verdict: a branch that resolved a position
1020
+ // (new flow, or a step of this one) overrides the router's proposal.
1021
+ isFlowComplete: stepResult.isFlowComplete,
1022
+ endedFlows: routingResult.endedFlows,
892
1023
  };
893
1024
  } catch (error) {
894
1025
  throw ResponseGenerationError.fromError(error, 'routing_optimization', params);
@@ -1003,12 +1134,19 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
1003
1134
  }
1004
1135
 
1005
1136
  /**
1006
- * Routing skip optimization (Requirements 20.1, 20.2, 20.3):
1007
- * When the current step declares `collect` fields AND pre-extraction populates
1008
- * at least one of those fields from the user's message, skip routing for this turn.
1137
+ * Routing skip check (Requirements 20.1, 20.2, 20.3):
1138
+ * When the current step declares `collect` fields AND step-scoped extraction
1139
+ * populates at least one of those fields from the user's message, the caller
1140
+ * skips routing for this turn (`skipResult`).
1141
+ *
1142
+ * The extraction is scoped to EXACTLY this step's collect fields — incidental
1143
+ * mentions of other (e.g. other-flow) fields cannot pin the routing decision.
1144
+ * It is skipped entirely when every collect field is already populated.
1009
1145
  *
1010
- * Returns the routing result if the skip applies, or undefined to fall through
1011
- * to normal routing.
1146
+ * Always returns what the check did so the caller can reuse the single
1147
+ * extraction result in its normal path instead of extracting twice:
1148
+ * - `extractionRan` — whether an extraction LLM call was made this turn.
1149
+ * - `extractedData` — that call's result (empty when nothing found / not run).
1012
1150
  */
1013
1151
  private async attemptRoutingSkipForCollect(params: {
1014
1152
  session: SessionState<TData>;
@@ -1016,22 +1154,27 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
1016
1154
  context: TContext;
1017
1155
  signal?: AbortSignal;
1018
1156
  }): Promise<{
1019
- selectedFlow?: Flow<TContext, TData>;
1020
- selectedStep?: Step<TContext, TData>;
1021
- responseDirectives?: string[];
1022
- session: SessionState<TData>;
1023
- isFlowComplete: boolean;
1024
- } | undefined> {
1157
+ extractionRan: boolean;
1158
+ extractedData: Partial<TData>;
1159
+ skipResult?: {
1160
+ selectedFlow?: Flow<TContext, TData>;
1161
+ selectedStep?: Step<TContext, TData>;
1162
+ responseDirectives?: string[];
1163
+ session: SessionState<TData>;
1164
+ isFlowComplete: boolean;
1165
+ };
1166
+ }> {
1025
1167
  const { session } = params;
1168
+ const notApplicable = { extractionRan: false, extractedData: {} as Partial<TData> };
1026
1169
 
1027
1170
  // Only applies when we already have a current flow and step
1028
1171
  if (!session.currentFlow || !session.currentStep) {
1029
- return undefined;
1172
+ return notApplicable;
1030
1173
  }
1031
1174
 
1032
1175
  // Also skip this optimization if there's a pending directive (it takes priority)
1033
1176
  if (session.pendingDirective) {
1034
- return undefined;
1177
+ return notApplicable;
1035
1178
  }
1036
1179
 
1037
1180
  // Look up the actual Flow and Step objects to access `collect`
@@ -1039,40 +1182,54 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
1039
1182
  (f) => f.id === session.currentFlow?.id
1040
1183
  );
1041
1184
  if (!currentFlow) {
1042
- return undefined;
1185
+ return notApplicable;
1043
1186
  }
1044
1187
 
1045
1188
  const currentStep = currentFlow.getStep(session.currentStep.id);
1046
1189
  if (!currentStep || !currentStep.collect || currentStep.collect.length === 0) {
1047
- return undefined;
1190
+ return notApplicable;
1048
1191
  }
1049
1192
 
1050
- // We have a step with collect fields. Run pre-extraction to see if the
1051
- // user's message populates any of them.
1052
1193
  const collectFields = currentStep.collect;
1053
1194
 
1195
+ // Every relevant field already set — the skip cannot fire; don't spend
1196
+ // an extraction LLM call probing for it.
1197
+ const sessionRecord = (session.data ?? {}) as Record<string, unknown>;
1198
+ const allPopulated = collectFields.every((field) => {
1199
+ const value = sessionRecord[String(field)];
1200
+ return value !== undefined && value !== null;
1201
+ });
1202
+ if (allPopulated) {
1203
+ return notApplicable;
1204
+ }
1205
+
1054
1206
  // Snapshot current data for comparison
1055
- const dataBefore = { ...session.data };
1207
+ const dataBefore = { ...sessionRecord } as Record<string, unknown>;
1056
1208
 
1057
- // Run pre-extraction against the current flow
1209
+ // Step-scoped pre-extraction: see if the user's message populates any of
1210
+ // THIS step's collect fields.
1058
1211
  const extractedData = await this.preExtractFlowData({
1059
1212
  route: currentFlow,
1060
1213
  history: params.history,
1061
1214
  context: params.context,
1062
1215
  session,
1063
1216
  signal: params.signal,
1217
+ restrictToFields: collectFields.map(String),
1064
1218
  });
1065
1219
 
1066
1220
  if (!extractedData || Object.keys(extractedData).length === 0) {
1067
- return undefined;
1221
+ return { extractionRan: true, extractedData: {} };
1068
1222
  }
1069
1223
 
1070
1224
  // Determine which collect fields were newly populated by pre-extraction
1225
+ const extractedRecord = extractedData as Record<string, unknown>;
1071
1226
  const populatedCollectFields: string[] = [];
1072
1227
  for (const field of collectFields) {
1073
- const key = field as string;
1074
- const hadValue = dataBefore[field] !== undefined && dataBefore[field] !== null;
1075
- const hasNewValue = extractedData[field] !== undefined && extractedData[field] !== null;
1228
+ const key = String(field);
1229
+ const hadValue =
1230
+ dataBefore[key] !== undefined && dataBefore[key] !== null;
1231
+ const hasNewValue =
1232
+ extractedRecord[key] !== undefined && extractedRecord[key] !== null;
1076
1233
  if (hasNewValue && !hadValue) {
1077
1234
  populatedCollectFields.push(key);
1078
1235
  }
@@ -1080,7 +1237,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
1080
1237
 
1081
1238
  if (populatedCollectFields.length === 0) {
1082
1239
  // Pre-extraction didn't populate any declared collect field — no skip
1083
- return undefined;
1240
+ return { extractionRan: true, extractedData };
1084
1241
  }
1085
1242
 
1086
1243
  // ROUTING SKIP: pre-extraction populated collect fields → retain current flow/step
@@ -1103,11 +1260,15 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
1103
1260
  });
1104
1261
 
1105
1262
  return {
1106
- selectedFlow: stepResult.flowChanged || currentFlow,
1107
- selectedStep: stepResult.nextStep,
1108
- responseDirectives: undefined,
1109
- session: stepResult.session,
1110
- isFlowComplete: false,
1263
+ extractionRan: true,
1264
+ extractedData,
1265
+ skipResult: {
1266
+ selectedFlow: stepResult.flowChanged || currentFlow,
1267
+ selectedStep: stepResult.nextStep,
1268
+ responseDirectives: undefined,
1269
+ session: stepResult.session,
1270
+ isFlowComplete: stepResult.isFlowComplete,
1271
+ },
1111
1272
  };
1112
1273
  }
1113
1274
 
@@ -1132,6 +1293,30 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
1132
1293
  return hasDataCollectionSteps;
1133
1294
  }
1134
1295
 
1296
+ /**
1297
+ * Build an extraction schema limited to the given subset of agent schema
1298
+ * fields. Used by the routing-skip probe so only the current step's collect
1299
+ * fields are visible to the extractor.
1300
+ */
1301
+ private buildFieldRestrictedSchema(
1302
+ agentSchema: StructuredSchema,
1303
+ fields: string[],
1304
+ ): StructuredSchema {
1305
+ const properties: Record<string, StructuredSchema> = {};
1306
+ for (const field of fields) {
1307
+ const prop = agentSchema.properties?.[field];
1308
+ if (prop) {
1309
+ properties[field] = prop;
1310
+ }
1311
+ }
1312
+ return {
1313
+ type: "object",
1314
+ description: `Extraction restricted to these fields: ${fields.join(', ')}`,
1315
+ properties,
1316
+ additionalProperties: false,
1317
+ };
1318
+ }
1319
+
1135
1320
  /**
1136
1321
  * Pre-extract data from user message when entering a flow
1137
1322
  * This allows skipping steps whose data is already provided
@@ -1142,15 +1327,20 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
1142
1327
  context: TContext;
1143
1328
  session: SessionState<TData>;
1144
1329
  signal?: AbortSignal;
1330
+ /** When set, extraction is restricted to exactly these schema fields (step-scoped routing-skip probe). */
1331
+ restrictToFields?: string[];
1145
1332
  }): Promise<Partial<TData>> {
1146
- const { route: flow, history, signal } = params;
1333
+ const { route: flow, history, signal, restrictToFields } = params;
1147
1334
 
1148
1335
  // Build a schema for data extraction based on flow's fields
1149
- const extractionSchema = this.getSchema();
1150
- if (!extractionSchema) {
1336
+ const agentSchema = this.getSchema();
1337
+ if (!agentSchema) {
1151
1338
  logger.warn(`[ResponsePipeline] No schema available for pre-extraction`);
1152
1339
  return {};
1153
1340
  }
1341
+ const extractionSchema = restrictToFields
1342
+ ? this.buildFieldRestrictedSchema(agentSchema, restrictToFields)
1343
+ : agentSchema;
1154
1344
 
1155
1345
  // Get last user message
1156
1346
  const lastMessage = getLastMessageFromHistory(history);
@@ -1166,11 +1356,15 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
1166
1356
  ];
1167
1357
 
1168
1358
  // Add field descriptions
1169
- if (flow.requiredFields) {
1170
- extractionPrompt.push(`Required fields: ${flow.requiredFields.join(', ')}`);
1171
- }
1172
- if (flow.optionalFields) {
1173
- extractionPrompt.push(`Optional fields: ${flow.optionalFields.join(', ')}`);
1359
+ if (restrictToFields) {
1360
+ extractionPrompt.push(`Only these fields: ${restrictToFields.join(', ')}`);
1361
+ } else {
1362
+ if (flow.requiredFields) {
1363
+ extractionPrompt.push(`Required fields: ${flow.requiredFields.join(', ')}`);
1364
+ }
1365
+ if (flow.optionalFields) {
1366
+ extractionPrompt.push(`Optional fields: ${flow.optionalFields.join(', ')}`);
1367
+ }
1174
1368
  }
1175
1369
 
1176
1370
  extractionPrompt.push(