@falai/agent 3.4.5 → 4.0.0-alpha.2

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 (856) hide show
  1. package/README.md +41 -34
  2. package/dist/cjs/core/Agent.d.ts +22 -378
  3. package/dist/cjs/core/Agent.d.ts.map +1 -1
  4. package/dist/cjs/core/Agent.js +104 -1178
  5. package/dist/cjs/core/Agent.js.map +1 -1
  6. package/dist/cjs/core/CompactionEngine.d.ts.map +1 -1
  7. package/dist/cjs/core/CompactionEngine.js +5 -3
  8. package/dist/cjs/core/CompactionEngine.js.map +1 -1
  9. package/dist/cjs/core/FlowSpec.d.ts +136 -0
  10. package/dist/cjs/core/FlowSpec.d.ts.map +1 -0
  11. package/dist/cjs/core/FlowSpec.js +522 -0
  12. package/dist/cjs/core/FlowSpec.js.map +1 -0
  13. package/dist/cjs/core/Migrate.d.ts +38 -0
  14. package/dist/cjs/core/Migrate.d.ts.map +1 -0
  15. package/dist/cjs/core/Migrate.js +270 -0
  16. package/dist/cjs/core/Migrate.js.map +1 -0
  17. package/dist/cjs/core/Prompt.d.ts +54 -0
  18. package/dist/cjs/core/Prompt.d.ts.map +1 -0
  19. package/dist/cjs/core/Prompt.js +143 -0
  20. package/dist/cjs/core/Prompt.js.map +1 -0
  21. package/dist/cjs/core/Runner.d.ts +160 -0
  22. package/dist/cjs/core/Runner.d.ts.map +1 -0
  23. package/dist/cjs/core/Runner.js +1131 -0
  24. package/dist/cjs/core/Runner.js.map +1 -0
  25. package/dist/cjs/core/Speak.d.ts +37 -0
  26. package/dist/cjs/core/Speak.d.ts.map +1 -0
  27. package/dist/cjs/core/Speak.js +364 -0
  28. package/dist/cjs/core/Speak.js.map +1 -0
  29. package/dist/cjs/core/Understand.d.ts +28 -0
  30. package/dist/cjs/core/Understand.d.ts.map +1 -0
  31. package/dist/cjs/core/Understand.js +353 -0
  32. package/dist/cjs/core/Understand.js.map +1 -0
  33. package/dist/cjs/core/contracts.d.ts +122 -0
  34. package/dist/cjs/core/contracts.d.ts.map +1 -0
  35. package/dist/cjs/core/contracts.js +11 -0
  36. package/dist/cjs/core/contracts.js.map +1 -0
  37. package/dist/cjs/core/falai.d.ts +57 -0
  38. package/dist/cjs/core/falai.d.ts.map +1 -0
  39. package/dist/cjs/core/falai.js +43 -0
  40. package/dist/cjs/core/falai.js.map +1 -0
  41. package/dist/cjs/core/predicate.d.ts +9 -0
  42. package/dist/cjs/core/predicate.d.ts.map +1 -0
  43. package/dist/cjs/core/predicate.js +58 -0
  44. package/dist/cjs/core/predicate.js.map +1 -0
  45. package/dist/cjs/index.d.ts +26 -31
  46. package/dist/cjs/index.d.ts.map +1 -1
  47. package/dist/cjs/index.js +46 -68
  48. package/dist/cjs/index.js.map +1 -1
  49. package/dist/cjs/persistence/MemoryStore.d.ts +15 -0
  50. package/dist/cjs/persistence/MemoryStore.d.ts.map +1 -0
  51. package/dist/cjs/persistence/MemoryStore.js +39 -0
  52. package/dist/cjs/persistence/MemoryStore.js.map +1 -0
  53. package/dist/cjs/persistence/MongoStore.d.ts +42 -0
  54. package/dist/cjs/persistence/MongoStore.d.ts.map +1 -0
  55. package/dist/cjs/persistence/MongoStore.js +60 -0
  56. package/dist/cjs/persistence/MongoStore.js.map +1 -0
  57. package/dist/cjs/persistence/OpenSearchStore.d.ts +86 -0
  58. package/dist/cjs/persistence/OpenSearchStore.d.ts.map +1 -0
  59. package/dist/cjs/persistence/OpenSearchStore.js +120 -0
  60. package/dist/cjs/persistence/OpenSearchStore.js.map +1 -0
  61. package/dist/cjs/persistence/PostgresStore.d.ts +41 -0
  62. package/dist/cjs/persistence/PostgresStore.d.ts.map +1 -0
  63. package/dist/cjs/persistence/PostgresStore.js +58 -0
  64. package/dist/cjs/persistence/PostgresStore.js.map +1 -0
  65. package/dist/cjs/persistence/PrismaStore.d.ts +65 -0
  66. package/dist/cjs/persistence/PrismaStore.d.ts.map +1 -0
  67. package/dist/cjs/persistence/PrismaStore.js +95 -0
  68. package/dist/cjs/persistence/PrismaStore.js.map +1 -0
  69. package/dist/cjs/persistence/RedisStore.d.ts +34 -0
  70. package/dist/cjs/persistence/RedisStore.d.ts.map +1 -0
  71. package/dist/cjs/persistence/RedisStore.js +61 -0
  72. package/dist/cjs/persistence/RedisStore.js.map +1 -0
  73. package/dist/cjs/persistence/SQLiteStore.d.ts +45 -0
  74. package/dist/cjs/persistence/SQLiteStore.d.ts.map +1 -0
  75. package/dist/cjs/persistence/SQLiteStore.js +74 -0
  76. package/dist/cjs/persistence/SQLiteStore.js.map +1 -0
  77. package/dist/cjs/persistence/sessionRow.d.ts +14 -0
  78. package/dist/cjs/persistence/sessionRow.d.ts.map +1 -0
  79. package/dist/cjs/persistence/sessionRow.js +50 -0
  80. package/dist/cjs/persistence/sessionRow.js.map +1 -0
  81. package/dist/cjs/providers/DeepSeekProvider.d.ts.map +1 -1
  82. package/dist/cjs/providers/DeepSeekProvider.js +8 -3
  83. package/dist/cjs/providers/DeepSeekProvider.js.map +1 -1
  84. package/dist/cjs/providers/GeminiProvider.d.ts +4 -3
  85. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  86. package/dist/cjs/providers/GeminiProvider.js +4 -3
  87. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  88. package/dist/cjs/providers/OpenAICompatibleProvider.d.ts +4 -0
  89. package/dist/cjs/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  90. package/dist/cjs/providers/OpenAICompatibleProvider.js +2 -0
  91. package/dist/cjs/providers/OpenAICompatibleProvider.js.map +1 -1
  92. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  93. package/dist/cjs/providers/OpenRouterProvider.js +2 -4
  94. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  95. package/dist/cjs/providers/ProviderAdapter.d.ts +11 -6
  96. package/dist/cjs/providers/ProviderAdapter.d.ts.map +1 -1
  97. package/dist/cjs/providers/ProviderAdapter.js +33 -10
  98. package/dist/cjs/providers/ProviderAdapter.js.map +1 -1
  99. package/dist/cjs/providers/ZaiProvider.d.ts +6 -4
  100. package/dist/cjs/providers/ZaiProvider.d.ts.map +1 -1
  101. package/dist/cjs/providers/ZaiProvider.js +6 -4
  102. package/dist/cjs/providers/ZaiProvider.js.map +1 -1
  103. package/dist/cjs/types/agent.d.ts +153 -383
  104. package/dist/cjs/types/agent.d.ts.map +1 -1
  105. package/dist/cjs/types/agent.js +1 -1
  106. package/dist/cjs/types/ai.d.ts +32 -1
  107. package/dist/cjs/types/ai.d.ts.map +1 -1
  108. package/dist/cjs/types/compaction.d.ts +3 -1
  109. package/dist/cjs/types/compaction.d.ts.map +1 -1
  110. package/dist/cjs/types/errors.d.ts +9 -12
  111. package/dist/cjs/types/errors.d.ts.map +1 -1
  112. package/dist/cjs/types/errors.js +14 -17
  113. package/dist/cjs/types/errors.js.map +1 -1
  114. package/dist/cjs/types/flow.d.ts +265 -513
  115. package/dist/cjs/types/flow.d.ts.map +1 -1
  116. package/dist/cjs/types/flow.js +7 -1
  117. package/dist/cjs/types/flow.js.map +1 -1
  118. package/dist/cjs/types/history.d.ts +7 -18
  119. package/dist/cjs/types/history.d.ts.map +1 -1
  120. package/dist/cjs/types/history.js.map +1 -1
  121. package/dist/cjs/types/index.d.ts +9 -15
  122. package/dist/cjs/types/index.d.ts.map +1 -1
  123. package/dist/cjs/types/index.js +4 -14
  124. package/dist/cjs/types/index.js.map +1 -1
  125. package/dist/cjs/types/session.d.ts +94 -64
  126. package/dist/cjs/types/session.d.ts.map +1 -1
  127. package/dist/cjs/types/session.js +5 -1
  128. package/dist/cjs/types/session.js.map +1 -1
  129. package/dist/cjs/types/tool.d.ts +37 -207
  130. package/dist/cjs/types/tool.d.ts.map +1 -1
  131. package/dist/cjs/types/tool.js +5 -14
  132. package/dist/cjs/types/tool.js.map +1 -1
  133. package/dist/cjs/utils/clock.d.ts +28 -0
  134. package/dist/cjs/utils/clock.d.ts.map +1 -0
  135. package/dist/cjs/utils/clock.js +64 -0
  136. package/dist/cjs/utils/clock.js.map +1 -0
  137. package/dist/cjs/utils/duration.d.ts +11 -0
  138. package/dist/cjs/utils/duration.d.ts.map +1 -0
  139. package/dist/cjs/utils/duration.js +31 -0
  140. package/dist/cjs/utils/duration.js.map +1 -0
  141. package/dist/cjs/utils/history.d.ts +4 -1
  142. package/dist/cjs/utils/history.d.ts.map +1 -1
  143. package/dist/cjs/utils/history.js +2 -2
  144. package/dist/cjs/utils/history.js.map +1 -1
  145. package/dist/cjs/utils/index.d.ts +4 -10
  146. package/dist/cjs/utils/index.d.ts.map +1 -1
  147. package/dist/cjs/utils/index.js +14 -61
  148. package/dist/cjs/utils/index.js.map +1 -1
  149. package/dist/cjs/utils/json.d.ts +2 -0
  150. package/dist/cjs/utils/json.d.ts.map +1 -1
  151. package/dist/cjs/utils/json.js +5 -0
  152. package/dist/cjs/utils/json.js.map +1 -1
  153. package/dist/cjs/utils/outcomes.d.ts +48 -0
  154. package/dist/cjs/utils/outcomes.d.ts.map +1 -0
  155. package/dist/cjs/utils/outcomes.js +51 -0
  156. package/dist/cjs/utils/outcomes.js.map +1 -0
  157. package/dist/cjs/utils/schema.d.ts +50 -0
  158. package/dist/cjs/utils/schema.d.ts.map +1 -0
  159. package/dist/cjs/utils/schema.js +138 -0
  160. package/dist/cjs/utils/schema.js.map +1 -0
  161. package/dist/cjs/utils/streamingMessage.d.ts +3 -2
  162. package/dist/cjs/utils/streamingMessage.d.ts.map +1 -1
  163. package/dist/cjs/utils/streamingMessage.js +38 -4
  164. package/dist/cjs/utils/streamingMessage.js.map +1 -1
  165. package/dist/cjs/utils/template.d.ts +13 -149
  166. package/dist/cjs/utils/template.d.ts.map +1 -1
  167. package/dist/cjs/utils/template.js +31 -363
  168. package/dist/cjs/utils/template.js.map +1 -1
  169. package/dist/cjs/utils/usage.d.ts +19 -0
  170. package/dist/cjs/utils/usage.d.ts.map +1 -0
  171. package/dist/cjs/utils/usage.js +35 -0
  172. package/dist/cjs/utils/usage.js.map +1 -0
  173. package/dist/core/Agent.d.ts +22 -378
  174. package/dist/core/Agent.d.ts.map +1 -1
  175. package/dist/core/Agent.js +107 -1181
  176. package/dist/core/Agent.js.map +1 -1
  177. package/dist/core/CompactionEngine.d.ts.map +1 -1
  178. package/dist/core/CompactionEngine.js +5 -3
  179. package/dist/core/CompactionEngine.js.map +1 -1
  180. package/dist/core/FlowSpec.d.ts +136 -0
  181. package/dist/core/FlowSpec.d.ts.map +1 -0
  182. package/dist/core/FlowSpec.js +516 -0
  183. package/dist/core/FlowSpec.js.map +1 -0
  184. package/dist/core/Migrate.d.ts +38 -0
  185. package/dist/core/Migrate.d.ts.map +1 -0
  186. package/dist/core/Migrate.js +264 -0
  187. package/dist/core/Migrate.js.map +1 -0
  188. package/dist/core/Prompt.d.ts +54 -0
  189. package/dist/core/Prompt.d.ts.map +1 -0
  190. package/dist/core/Prompt.js +133 -0
  191. package/dist/core/Prompt.js.map +1 -0
  192. package/dist/core/Runner.d.ts +160 -0
  193. package/dist/core/Runner.d.ts.map +1 -0
  194. package/dist/core/Runner.js +1127 -0
  195. package/dist/core/Runner.js.map +1 -0
  196. package/dist/core/Speak.d.ts +37 -0
  197. package/dist/core/Speak.d.ts.map +1 -0
  198. package/dist/core/Speak.js +360 -0
  199. package/dist/core/Speak.js.map +1 -0
  200. package/dist/core/Understand.d.ts +28 -0
  201. package/dist/core/Understand.d.ts.map +1 -0
  202. package/dist/core/Understand.js +349 -0
  203. package/dist/core/Understand.js.map +1 -0
  204. package/dist/core/contracts.d.ts +122 -0
  205. package/dist/core/contracts.d.ts.map +1 -0
  206. package/dist/core/contracts.js +10 -0
  207. package/dist/core/contracts.js.map +1 -0
  208. package/dist/core/falai.d.ts +57 -0
  209. package/dist/core/falai.d.ts.map +1 -0
  210. package/dist/core/falai.js +40 -0
  211. package/dist/core/falai.js.map +1 -0
  212. package/dist/core/predicate.d.ts +9 -0
  213. package/dist/core/predicate.d.ts.map +1 -0
  214. package/dist/core/predicate.js +54 -0
  215. package/dist/core/predicate.js.map +1 -0
  216. package/dist/index.d.ts +26 -31
  217. package/dist/index.d.ts.map +1 -1
  218. package/dist/index.js +19 -24
  219. package/dist/index.js.map +1 -1
  220. package/dist/persistence/MemoryStore.d.ts +15 -0
  221. package/dist/persistence/MemoryStore.d.ts.map +1 -0
  222. package/dist/persistence/MemoryStore.js +35 -0
  223. package/dist/persistence/MemoryStore.js.map +1 -0
  224. package/dist/persistence/MongoStore.d.ts +42 -0
  225. package/dist/persistence/MongoStore.d.ts.map +1 -0
  226. package/dist/persistence/MongoStore.js +56 -0
  227. package/dist/persistence/MongoStore.js.map +1 -0
  228. package/dist/persistence/OpenSearchStore.d.ts +86 -0
  229. package/dist/persistence/OpenSearchStore.d.ts.map +1 -0
  230. package/dist/persistence/OpenSearchStore.js +116 -0
  231. package/dist/persistence/OpenSearchStore.js.map +1 -0
  232. package/dist/persistence/PostgresStore.d.ts +41 -0
  233. package/dist/persistence/PostgresStore.d.ts.map +1 -0
  234. package/dist/persistence/PostgresStore.js +54 -0
  235. package/dist/persistence/PostgresStore.js.map +1 -0
  236. package/dist/persistence/PrismaStore.d.ts +65 -0
  237. package/dist/persistence/PrismaStore.d.ts.map +1 -0
  238. package/dist/persistence/PrismaStore.js +91 -0
  239. package/dist/persistence/PrismaStore.js.map +1 -0
  240. package/dist/persistence/RedisStore.d.ts +34 -0
  241. package/dist/persistence/RedisStore.d.ts.map +1 -0
  242. package/dist/persistence/RedisStore.js +57 -0
  243. package/dist/persistence/RedisStore.js.map +1 -0
  244. package/dist/persistence/SQLiteStore.d.ts +45 -0
  245. package/dist/persistence/SQLiteStore.d.ts.map +1 -0
  246. package/dist/persistence/SQLiteStore.js +70 -0
  247. package/dist/persistence/SQLiteStore.js.map +1 -0
  248. package/dist/persistence/sessionRow.d.ts +14 -0
  249. package/dist/persistence/sessionRow.d.ts.map +1 -0
  250. package/dist/persistence/sessionRow.js +45 -0
  251. package/dist/persistence/sessionRow.js.map +1 -0
  252. package/dist/providers/DeepSeekProvider.d.ts.map +1 -1
  253. package/dist/providers/DeepSeekProvider.js +8 -3
  254. package/dist/providers/DeepSeekProvider.js.map +1 -1
  255. package/dist/providers/GeminiProvider.d.ts +4 -3
  256. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  257. package/dist/providers/GeminiProvider.js +4 -3
  258. package/dist/providers/GeminiProvider.js.map +1 -1
  259. package/dist/providers/OpenAICompatibleProvider.d.ts +4 -0
  260. package/dist/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  261. package/dist/providers/OpenAICompatibleProvider.js +2 -0
  262. package/dist/providers/OpenAICompatibleProvider.js.map +1 -1
  263. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  264. package/dist/providers/OpenRouterProvider.js +2 -4
  265. package/dist/providers/OpenRouterProvider.js.map +1 -1
  266. package/dist/providers/ProviderAdapter.d.ts +11 -6
  267. package/dist/providers/ProviderAdapter.d.ts.map +1 -1
  268. package/dist/providers/ProviderAdapter.js +34 -11
  269. package/dist/providers/ProviderAdapter.js.map +1 -1
  270. package/dist/providers/ZaiProvider.d.ts +6 -4
  271. package/dist/providers/ZaiProvider.d.ts.map +1 -1
  272. package/dist/providers/ZaiProvider.js +6 -4
  273. package/dist/providers/ZaiProvider.js.map +1 -1
  274. package/dist/types/agent.d.ts +153 -383
  275. package/dist/types/agent.d.ts.map +1 -1
  276. package/dist/types/agent.js +1 -1
  277. package/dist/types/ai.d.ts +32 -1
  278. package/dist/types/ai.d.ts.map +1 -1
  279. package/dist/types/compaction.d.ts +3 -1
  280. package/dist/types/compaction.d.ts.map +1 -1
  281. package/dist/types/errors.d.ts +9 -12
  282. package/dist/types/errors.d.ts.map +1 -1
  283. package/dist/types/errors.js +12 -15
  284. package/dist/types/errors.js.map +1 -1
  285. package/dist/types/flow.d.ts +265 -513
  286. package/dist/types/flow.d.ts.map +1 -1
  287. package/dist/types/flow.js +7 -1
  288. package/dist/types/flow.js.map +1 -1
  289. package/dist/types/history.d.ts +7 -18
  290. package/dist/types/history.d.ts.map +1 -1
  291. package/dist/types/history.js.map +1 -1
  292. package/dist/types/index.d.ts +9 -15
  293. package/dist/types/index.d.ts.map +1 -1
  294. package/dist/types/index.js +2 -7
  295. package/dist/types/index.js.map +1 -1
  296. package/dist/types/session.d.ts +94 -64
  297. package/dist/types/session.d.ts.map +1 -1
  298. package/dist/types/session.js +5 -1
  299. package/dist/types/session.js.map +1 -1
  300. package/dist/types/tool.d.ts +37 -207
  301. package/dist/types/tool.d.ts.map +1 -1
  302. package/dist/types/tool.js +6 -13
  303. package/dist/types/tool.js.map +1 -1
  304. package/dist/utils/clock.d.ts +28 -0
  305. package/dist/utils/clock.d.ts.map +1 -0
  306. package/dist/utils/clock.js +59 -0
  307. package/dist/utils/clock.js.map +1 -0
  308. package/dist/utils/duration.d.ts +11 -0
  309. package/dist/utils/duration.d.ts.map +1 -0
  310. package/dist/utils/duration.js +26 -0
  311. package/dist/utils/duration.js.map +1 -0
  312. package/dist/utils/history.d.ts +4 -1
  313. package/dist/utils/history.d.ts.map +1 -1
  314. package/dist/utils/history.js +2 -2
  315. package/dist/utils/history.js.map +1 -1
  316. package/dist/utils/index.d.ts +4 -10
  317. package/dist/utils/index.d.ts.map +1 -1
  318. package/dist/utils/index.js +4 -21
  319. package/dist/utils/index.js.map +1 -1
  320. package/dist/utils/json.d.ts +2 -0
  321. package/dist/utils/json.d.ts.map +1 -1
  322. package/dist/utils/json.js +4 -0
  323. package/dist/utils/json.js.map +1 -1
  324. package/dist/utils/outcomes.d.ts +48 -0
  325. package/dist/utils/outcomes.d.ts.map +1 -0
  326. package/dist/utils/outcomes.js +48 -0
  327. package/dist/utils/outcomes.js.map +1 -0
  328. package/dist/utils/schema.d.ts +50 -0
  329. package/dist/utils/schema.d.ts.map +1 -0
  330. package/dist/utils/schema.js +129 -0
  331. package/dist/utils/schema.js.map +1 -0
  332. package/dist/utils/streamingMessage.d.ts +3 -2
  333. package/dist/utils/streamingMessage.d.ts.map +1 -1
  334. package/dist/utils/streamingMessage.js +38 -4
  335. package/dist/utils/streamingMessage.js.map +1 -1
  336. package/dist/utils/template.d.ts +13 -149
  337. package/dist/utils/template.d.ts.map +1 -1
  338. package/dist/utils/template.js +28 -355
  339. package/dist/utils/template.js.map +1 -1
  340. package/dist/utils/usage.d.ts +19 -0
  341. package/dist/utils/usage.d.ts.map +1 -0
  342. package/dist/utils/usage.js +31 -0
  343. package/dist/utils/usage.js.map +1 -0
  344. package/docs/README.md +37 -19
  345. package/docs/concepts/architecture.md +117 -239
  346. package/docs/concepts/collection.md +170 -0
  347. package/docs/concepts/pipeline.md +132 -378
  348. package/docs/concepts/runs-and-waits.md +192 -0
  349. package/docs/guides/actions-and-events.md +276 -0
  350. package/docs/guides/branching.md +119 -208
  351. package/docs/guides/compaction.md +63 -158
  352. package/docs/guides/conditions.md +164 -128
  353. package/docs/guides/error-handling.md +168 -164
  354. package/docs/guides/flow-control.md +210 -349
  355. package/docs/guides/flows-from-json.md +224 -0
  356. package/docs/guides/instructions.md +125 -161
  357. package/docs/guides/persistence.md +182 -206
  358. package/docs/guides/streaming.md +50 -114
  359. package/docs/guides/testing.md +284 -0
  360. package/docs/guides/triggers.md +401 -0
  361. package/docs/migration/README.md +8 -15
  362. package/docs/migration/v1-to-v2.md +1 -1
  363. package/docs/migration/v2-3-to-v2-4.md +2 -2
  364. package/docs/migration/v2-6-to-v2-7.md +4 -4
  365. package/docs/migration/v3-to-v4.md +452 -0
  366. package/docs/reference/actions-events-conditions.md +396 -0
  367. package/docs/reference/agent.md +244 -0
  368. package/docs/reference/branches.md +75 -203
  369. package/docs/reference/errors.md +188 -144
  370. package/docs/reference/fields.md +125 -0
  371. package/docs/reference/flow-spec.md +248 -0
  372. package/docs/reference/flow.md +104 -192
  373. package/docs/reference/instruction.md +83 -137
  374. package/docs/reference/outcomes.md +273 -0
  375. package/docs/reference/providers.md +525 -302
  376. package/docs/reference/session.md +210 -0
  377. package/docs/reference/step.md +194 -312
  378. package/docs/reference/stores.md +496 -0
  379. package/docs/reference/tool.md +162 -231
  380. package/docs/reference/trigger.md +180 -0
  381. package/docs/rfc/v4-one-flow.md +477 -0
  382. package/docs/start/01-install.md +59 -44
  383. package/docs/start/02-first-agent.md +97 -147
  384. package/docs/start/03-collect-data.md +78 -183
  385. package/docs/start/04-add-tools.md +159 -227
  386. package/docs/start/05-go-to-production.md +167 -164
  387. package/examples/01-quickstart.ts +26 -16
  388. package/examples/02-fields.ts +75 -0
  389. package/examples/03-tools.ts +79 -119
  390. package/examples/04-instructions.ts +60 -87
  391. package/examples/05-branches.ts +78 -0
  392. package/examples/06-triggers-and-waits.ts +148 -0
  393. package/examples/07-streaming.ts +34 -60
  394. package/examples/08-store-and-migration.ts +97 -0
  395. package/examples/09-flows-from-json.ts +107 -0
  396. package/package.json +9 -6
  397. package/src/core/Agent.ts +116 -1512
  398. package/src/core/CompactionEngine.ts +7 -4
  399. package/src/core/FlowSpec.ts +712 -0
  400. package/src/core/Migrate.ts +256 -0
  401. package/src/core/Prompt.ts +156 -0
  402. package/src/core/Runner.ts +1181 -0
  403. package/src/core/Speak.ts +451 -0
  404. package/src/core/Understand.ts +422 -0
  405. package/src/core/contracts.ts +111 -0
  406. package/src/core/falai.ts +86 -0
  407. package/src/core/predicate.ts +56 -0
  408. package/src/index.ts +119 -147
  409. package/src/persistence/MemoryStore.ts +37 -0
  410. package/src/persistence/MongoStore.ts +89 -0
  411. package/src/persistence/OpenSearchStore.ts +153 -0
  412. package/src/persistence/PostgresStore.ts +89 -0
  413. package/src/persistence/PrismaStore.ts +127 -0
  414. package/src/persistence/RedisStore.ts +90 -0
  415. package/src/persistence/SQLiteStore.ts +103 -0
  416. package/src/persistence/sessionRow.ts +45 -0
  417. package/src/providers/DeepSeekProvider.ts +8 -3
  418. package/src/providers/GeminiProvider.ts +4 -3
  419. package/src/providers/OpenAICompatibleProvider.ts +6 -0
  420. package/src/providers/OpenRouterProvider.ts +2 -4
  421. package/src/providers/ProviderAdapter.ts +46 -13
  422. package/src/providers/ZaiProvider.ts +6 -4
  423. package/src/types/agent.ts +124 -397
  424. package/src/types/ai.ts +33 -1
  425. package/src/types/compaction.ts +3 -1
  426. package/src/types/errors.ts +13 -16
  427. package/src/types/flow.ts +249 -550
  428. package/src/types/history.ts +7 -20
  429. package/src/types/index.ts +87 -139
  430. package/src/types/session.ts +135 -70
  431. package/src/types/tool.ts +42 -267
  432. package/src/utils/clock.ts +70 -0
  433. package/src/utils/duration.ts +33 -0
  434. package/src/utils/history.ts +3 -2
  435. package/src/utils/index.ts +8 -66
  436. package/src/utils/json.ts +5 -0
  437. package/src/utils/outcomes.ts +56 -0
  438. package/src/utils/schema.ts +145 -0
  439. package/src/utils/streamingMessage.ts +34 -4
  440. package/src/utils/template.ts +32 -423
  441. package/src/utils/usage.ts +37 -0
  442. package/dist/adapters/MemoryAdapter.d.ts +0 -47
  443. package/dist/adapters/MemoryAdapter.d.ts.map +0 -1
  444. package/dist/adapters/MemoryAdapter.js +0 -204
  445. package/dist/adapters/MemoryAdapter.js.map +0 -1
  446. package/dist/adapters/MongoAdapter.d.ts +0 -97
  447. package/dist/adapters/MongoAdapter.d.ts.map +0 -1
  448. package/dist/adapters/MongoAdapter.js +0 -196
  449. package/dist/adapters/MongoAdapter.js.map +0 -1
  450. package/dist/adapters/OpenSearchAdapter.d.ts +0 -169
  451. package/dist/adapters/OpenSearchAdapter.d.ts.map +0 -1
  452. package/dist/adapters/OpenSearchAdapter.js +0 -471
  453. package/dist/adapters/OpenSearchAdapter.js.map +0 -1
  454. package/dist/adapters/PostgreSQLAdapter.d.ts +0 -85
  455. package/dist/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  456. package/dist/adapters/PostgreSQLAdapter.js +0 -308
  457. package/dist/adapters/PostgreSQLAdapter.js.map +0 -1
  458. package/dist/adapters/PrismaAdapter.d.ts +0 -115
  459. package/dist/adapters/PrismaAdapter.d.ts.map +0 -1
  460. package/dist/adapters/PrismaAdapter.js +0 -406
  461. package/dist/adapters/PrismaAdapter.js.map +0 -1
  462. package/dist/adapters/RedisAdapter.d.ts +0 -72
  463. package/dist/adapters/RedisAdapter.d.ts.map +0 -1
  464. package/dist/adapters/RedisAdapter.js +0 -286
  465. package/dist/adapters/RedisAdapter.js.map +0 -1
  466. package/dist/adapters/SQLiteAdapter.d.ts +0 -86
  467. package/dist/adapters/SQLiteAdapter.d.ts.map +0 -1
  468. package/dist/adapters/SQLiteAdapter.js +0 -337
  469. package/dist/adapters/SQLiteAdapter.js.map +0 -1
  470. package/dist/adapters/index.d.ts +0 -17
  471. package/dist/adapters/index.d.ts.map +0 -1
  472. package/dist/adapters/index.js +0 -11
  473. package/dist/adapters/index.js.map +0 -1
  474. package/dist/adapters/sessionRow.d.ts +0 -22
  475. package/dist/adapters/sessionRow.d.ts.map +0 -1
  476. package/dist/adapters/sessionRow.js +0 -48
  477. package/dist/adapters/sessionRow.js.map +0 -1
  478. package/dist/cjs/adapters/MemoryAdapter.d.ts +0 -47
  479. package/dist/cjs/adapters/MemoryAdapter.d.ts.map +0 -1
  480. package/dist/cjs/adapters/MemoryAdapter.js +0 -208
  481. package/dist/cjs/adapters/MemoryAdapter.js.map +0 -1
  482. package/dist/cjs/adapters/MongoAdapter.d.ts +0 -97
  483. package/dist/cjs/adapters/MongoAdapter.d.ts.map +0 -1
  484. package/dist/cjs/adapters/MongoAdapter.js +0 -200
  485. package/dist/cjs/adapters/MongoAdapter.js.map +0 -1
  486. package/dist/cjs/adapters/OpenSearchAdapter.d.ts +0 -169
  487. package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +0 -1
  488. package/dist/cjs/adapters/OpenSearchAdapter.js +0 -475
  489. package/dist/cjs/adapters/OpenSearchAdapter.js.map +0 -1
  490. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +0 -85
  491. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  492. package/dist/cjs/adapters/PostgreSQLAdapter.js +0 -312
  493. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +0 -1
  494. package/dist/cjs/adapters/PrismaAdapter.d.ts +0 -115
  495. package/dist/cjs/adapters/PrismaAdapter.d.ts.map +0 -1
  496. package/dist/cjs/adapters/PrismaAdapter.js +0 -410
  497. package/dist/cjs/adapters/PrismaAdapter.js.map +0 -1
  498. package/dist/cjs/adapters/RedisAdapter.d.ts +0 -72
  499. package/dist/cjs/adapters/RedisAdapter.d.ts.map +0 -1
  500. package/dist/cjs/adapters/RedisAdapter.js +0 -290
  501. package/dist/cjs/adapters/RedisAdapter.js.map +0 -1
  502. package/dist/cjs/adapters/SQLiteAdapter.d.ts +0 -86
  503. package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +0 -1
  504. package/dist/cjs/adapters/SQLiteAdapter.js +0 -341
  505. package/dist/cjs/adapters/SQLiteAdapter.js.map +0 -1
  506. package/dist/cjs/adapters/index.d.ts +0 -17
  507. package/dist/cjs/adapters/index.d.ts.map +0 -1
  508. package/dist/cjs/adapters/index.js +0 -21
  509. package/dist/cjs/adapters/index.js.map +0 -1
  510. package/dist/cjs/adapters/sessionRow.d.ts +0 -22
  511. package/dist/cjs/adapters/sessionRow.d.ts.map +0 -1
  512. package/dist/cjs/adapters/sessionRow.js +0 -52
  513. package/dist/cjs/adapters/sessionRow.js.map +0 -1
  514. package/dist/cjs/constants/index.d.ts +0 -1
  515. package/dist/cjs/constants/index.d.ts.map +0 -1
  516. package/dist/cjs/constants/index.js +0 -4
  517. package/dist/cjs/constants/index.js.map +0 -1
  518. package/dist/cjs/core/AutoChainExecutor.d.ts +0 -97
  519. package/dist/cjs/core/AutoChainExecutor.d.ts.map +0 -1
  520. package/dist/cjs/core/AutoChainExecutor.js +0 -288
  521. package/dist/cjs/core/AutoChainExecutor.js.map +0 -1
  522. package/dist/cjs/core/BranchEvaluator.d.ts +0 -55
  523. package/dist/cjs/core/BranchEvaluator.d.ts.map +0 -1
  524. package/dist/cjs/core/BranchEvaluator.js +0 -125
  525. package/dist/cjs/core/BranchEvaluator.js.map +0 -1
  526. package/dist/cjs/core/DirectiveChainTracker.d.ts +0 -49
  527. package/dist/cjs/core/DirectiveChainTracker.d.ts.map +0 -1
  528. package/dist/cjs/core/DirectiveChainTracker.js +0 -121
  529. package/dist/cjs/core/DirectiveChainTracker.js.map +0 -1
  530. package/dist/cjs/core/Events.d.ts +0 -26
  531. package/dist/cjs/core/Events.d.ts.map +0 -1
  532. package/dist/cjs/core/Events.js +0 -144
  533. package/dist/cjs/core/Events.js.map +0 -1
  534. package/dist/cjs/core/Flow.d.ts +0 -183
  535. package/dist/cjs/core/Flow.d.ts.map +0 -1
  536. package/dist/cjs/core/Flow.js +0 -551
  537. package/dist/cjs/core/Flow.js.map +0 -1
  538. package/dist/cjs/core/FlowRouter.d.ts +0 -183
  539. package/dist/cjs/core/FlowRouter.d.ts.map +0 -1
  540. package/dist/cjs/core/FlowRouter.js +0 -1047
  541. package/dist/cjs/core/FlowRouter.js.map +0 -1
  542. package/dist/cjs/core/PersistenceManager.d.ts +0 -114
  543. package/dist/cjs/core/PersistenceManager.d.ts.map +0 -1
  544. package/dist/cjs/core/PersistenceManager.js +0 -336
  545. package/dist/cjs/core/PersistenceManager.js.map +0 -1
  546. package/dist/cjs/core/PromptComposer.d.ts +0 -47
  547. package/dist/cjs/core/PromptComposer.d.ts.map +0 -1
  548. package/dist/cjs/core/PromptComposer.js +0 -397
  549. package/dist/cjs/core/PromptComposer.js.map +0 -1
  550. package/dist/cjs/core/PromptSectionCache.d.ts +0 -48
  551. package/dist/cjs/core/PromptSectionCache.d.ts.map +0 -1
  552. package/dist/cjs/core/PromptSectionCache.js +0 -108
  553. package/dist/cjs/core/PromptSectionCache.js.map +0 -1
  554. package/dist/cjs/core/ResponseEngine.d.ts +0 -43
  555. package/dist/cjs/core/ResponseEngine.d.ts.map +0 -1
  556. package/dist/cjs/core/ResponseEngine.js +0 -235
  557. package/dist/cjs/core/ResponseEngine.js.map +0 -1
  558. package/dist/cjs/core/ResponseGenerationError.d.ts +0 -30
  559. package/dist/cjs/core/ResponseGenerationError.d.ts.map +0 -1
  560. package/dist/cjs/core/ResponseGenerationError.js +0 -35
  561. package/dist/cjs/core/ResponseGenerationError.js.map +0 -1
  562. package/dist/cjs/core/ResponseModal.d.ts +0 -305
  563. package/dist/cjs/core/ResponseModal.d.ts.map +0 -1
  564. package/dist/cjs/core/ResponseModal.js +0 -1414
  565. package/dist/cjs/core/ResponseModal.js.map +0 -1
  566. package/dist/cjs/core/ResponsePipeline.d.ts +0 -220
  567. package/dist/cjs/core/ResponsePipeline.d.ts.map +0 -1
  568. package/dist/cjs/core/ResponsePipeline.js +0 -1040
  569. package/dist/cjs/core/ResponsePipeline.js.map +0 -1
  570. package/dist/cjs/core/SessionFinalizer.d.ts +0 -34
  571. package/dist/cjs/core/SessionFinalizer.d.ts.map +0 -1
  572. package/dist/cjs/core/SessionFinalizer.js +0 -88
  573. package/dist/cjs/core/SessionFinalizer.js.map +0 -1
  574. package/dist/cjs/core/SessionManager.d.ts +0 -112
  575. package/dist/cjs/core/SessionManager.d.ts.map +0 -1
  576. package/dist/cjs/core/SessionManager.js +0 -308
  577. package/dist/cjs/core/SessionManager.js.map +0 -1
  578. package/dist/cjs/core/SignalCoordinator.d.ts +0 -103
  579. package/dist/cjs/core/SignalCoordinator.d.ts.map +0 -1
  580. package/dist/cjs/core/SignalCoordinator.js +0 -207
  581. package/dist/cjs/core/SignalCoordinator.js.map +0 -1
  582. package/dist/cjs/core/SignalEvaluator.d.ts +0 -86
  583. package/dist/cjs/core/SignalEvaluator.d.ts.map +0 -1
  584. package/dist/cjs/core/SignalEvaluator.js +0 -319
  585. package/dist/cjs/core/SignalEvaluator.js.map +0 -1
  586. package/dist/cjs/core/SignalProcessor.d.ts +0 -152
  587. package/dist/cjs/core/SignalProcessor.d.ts.map +0 -1
  588. package/dist/cjs/core/SignalProcessor.js +0 -505
  589. package/dist/cjs/core/SignalProcessor.js.map +0 -1
  590. package/dist/cjs/core/Step.d.ts +0 -184
  591. package/dist/cjs/core/Step.d.ts.map +0 -1
  592. package/dist/cjs/core/Step.js +0 -599
  593. package/dist/cjs/core/Step.js.map +0 -1
  594. package/dist/cjs/core/StepLifecycle.d.ts +0 -43
  595. package/dist/cjs/core/StepLifecycle.d.ts.map +0 -1
  596. package/dist/cjs/core/StepLifecycle.js +0 -180
  597. package/dist/cjs/core/StepLifecycle.js.map +0 -1
  598. package/dist/cjs/core/StreamingToolExecutor.d.ts +0 -142
  599. package/dist/cjs/core/StreamingToolExecutor.d.ts.map +0 -1
  600. package/dist/cjs/core/StreamingToolExecutor.js +0 -490
  601. package/dist/cjs/core/StreamingToolExecutor.js.map +0 -1
  602. package/dist/cjs/core/ToolLoopExecutor.d.ts +0 -133
  603. package/dist/cjs/core/ToolLoopExecutor.d.ts.map +0 -1
  604. package/dist/cjs/core/ToolLoopExecutor.js +0 -568
  605. package/dist/cjs/core/ToolLoopExecutor.js.map +0 -1
  606. package/dist/cjs/core/ToolManager.d.ts +0 -250
  607. package/dist/cjs/core/ToolManager.d.ts.map +0 -1
  608. package/dist/cjs/core/ToolManager.js +0 -1104
  609. package/dist/cjs/core/ToolManager.js.map +0 -1
  610. package/dist/cjs/core/createAgent.d.ts +0 -35
  611. package/dist/cjs/core/createAgent.d.ts.map +0 -1
  612. package/dist/cjs/core/createAgent.js +0 -39
  613. package/dist/cjs/core/createAgent.js.map +0 -1
  614. package/dist/cjs/core/flow-namespace.d.ts +0 -64
  615. package/dist/cjs/core/flow-namespace.d.ts.map +0 -1
  616. package/dist/cjs/core/flow-namespace.js +0 -182
  617. package/dist/cjs/core/flow-namespace.js.map +0 -1
  618. package/dist/cjs/core/toolGates.d.ts +0 -24
  619. package/dist/cjs/core/toolGates.d.ts.map +0 -1
  620. package/dist/cjs/core/toolGates.js +0 -52
  621. package/dist/cjs/core/toolGates.js.map +0 -1
  622. package/dist/cjs/types/persistence.d.ts +0 -254
  623. package/dist/cjs/types/persistence.d.ts.map +0 -1
  624. package/dist/cjs/types/persistence.js +0 -7
  625. package/dist/cjs/types/persistence.js.map +0 -1
  626. package/dist/cjs/types/prompt-cache.d.ts +0 -15
  627. package/dist/cjs/types/prompt-cache.d.ts.map +0 -1
  628. package/dist/cjs/types/prompt-cache.js +0 -6
  629. package/dist/cjs/types/prompt-cache.js.map +0 -1
  630. package/dist/cjs/types/signals.d.ts +0 -263
  631. package/dist/cjs/types/signals.d.ts.map +0 -1
  632. package/dist/cjs/types/signals.js +0 -11
  633. package/dist/cjs/types/signals.js.map +0 -1
  634. package/dist/cjs/types/template.d.ts +0 -84
  635. package/dist/cjs/types/template.d.ts.map +0 -1
  636. package/dist/cjs/types/template.js +0 -3
  637. package/dist/cjs/types/template.js.map +0 -1
  638. package/dist/cjs/utils/condition.d.ts +0 -63
  639. package/dist/cjs/utils/condition.d.ts.map +0 -1
  640. package/dist/cjs/utils/condition.js +0 -239
  641. package/dist/cjs/utils/condition.js.map +0 -1
  642. package/dist/cjs/utils/event.d.ts +0 -6
  643. package/dist/cjs/utils/event.d.ts.map +0 -1
  644. package/dist/cjs/utils/event.js +0 -20
  645. package/dist/cjs/utils/event.js.map +0 -1
  646. package/dist/cjs/utils/id.d.ts +0 -33
  647. package/dist/cjs/utils/id.d.ts.map +0 -1
  648. package/dist/cjs/utils/id.js +0 -84
  649. package/dist/cjs/utils/id.js.map +0 -1
  650. package/dist/cjs/utils/serialize.d.ts +0 -36
  651. package/dist/cjs/utils/serialize.d.ts.map +0 -1
  652. package/dist/cjs/utils/serialize.js +0 -77
  653. package/dist/cjs/utils/serialize.js.map +0 -1
  654. package/dist/cjs/utils/session.d.ts +0 -124
  655. package/dist/cjs/utils/session.d.ts.map +0 -1
  656. package/dist/cjs/utils/session.js +0 -396
  657. package/dist/cjs/utils/session.js.map +0 -1
  658. package/dist/constants/index.d.ts +0 -2
  659. package/dist/constants/index.d.ts.map +0 -1
  660. package/dist/constants/index.js +0 -4
  661. package/dist/constants/index.js.map +0 -1
  662. package/dist/core/AutoChainExecutor.d.ts +0 -97
  663. package/dist/core/AutoChainExecutor.d.ts.map +0 -1
  664. package/dist/core/AutoChainExecutor.js +0 -284
  665. package/dist/core/AutoChainExecutor.js.map +0 -1
  666. package/dist/core/BranchEvaluator.d.ts +0 -55
  667. package/dist/core/BranchEvaluator.d.ts.map +0 -1
  668. package/dist/core/BranchEvaluator.js +0 -121
  669. package/dist/core/BranchEvaluator.js.map +0 -1
  670. package/dist/core/DirectiveChainTracker.d.ts +0 -49
  671. package/dist/core/DirectiveChainTracker.d.ts.map +0 -1
  672. package/dist/core/DirectiveChainTracker.js +0 -117
  673. package/dist/core/DirectiveChainTracker.js.map +0 -1
  674. package/dist/core/Events.d.ts +0 -26
  675. package/dist/core/Events.d.ts.map +0 -1
  676. package/dist/core/Events.js +0 -137
  677. package/dist/core/Events.js.map +0 -1
  678. package/dist/core/Flow.d.ts +0 -183
  679. package/dist/core/Flow.d.ts.map +0 -1
  680. package/dist/core/Flow.js +0 -547
  681. package/dist/core/Flow.js.map +0 -1
  682. package/dist/core/FlowRouter.d.ts +0 -183
  683. package/dist/core/FlowRouter.d.ts.map +0 -1
  684. package/dist/core/FlowRouter.js +0 -1043
  685. package/dist/core/FlowRouter.js.map +0 -1
  686. package/dist/core/PersistenceManager.d.ts +0 -114
  687. package/dist/core/PersistenceManager.d.ts.map +0 -1
  688. package/dist/core/PersistenceManager.js +0 -332
  689. package/dist/core/PersistenceManager.js.map +0 -1
  690. package/dist/core/PromptComposer.d.ts +0 -47
  691. package/dist/core/PromptComposer.d.ts.map +0 -1
  692. package/dist/core/PromptComposer.js +0 -393
  693. package/dist/core/PromptComposer.js.map +0 -1
  694. package/dist/core/PromptSectionCache.d.ts +0 -48
  695. package/dist/core/PromptSectionCache.d.ts.map +0 -1
  696. package/dist/core/PromptSectionCache.js +0 -104
  697. package/dist/core/PromptSectionCache.js.map +0 -1
  698. package/dist/core/ResponseEngine.d.ts +0 -43
  699. package/dist/core/ResponseEngine.d.ts.map +0 -1
  700. package/dist/core/ResponseEngine.js +0 -231
  701. package/dist/core/ResponseEngine.js.map +0 -1
  702. package/dist/core/ResponseGenerationError.d.ts +0 -30
  703. package/dist/core/ResponseGenerationError.d.ts.map +0 -1
  704. package/dist/core/ResponseGenerationError.js +0 -31
  705. package/dist/core/ResponseGenerationError.js.map +0 -1
  706. package/dist/core/ResponseModal.d.ts +0 -305
  707. package/dist/core/ResponseModal.d.ts.map +0 -1
  708. package/dist/core/ResponseModal.js +0 -1410
  709. package/dist/core/ResponseModal.js.map +0 -1
  710. package/dist/core/ResponsePipeline.d.ts +0 -220
  711. package/dist/core/ResponsePipeline.d.ts.map +0 -1
  712. package/dist/core/ResponsePipeline.js +0 -1035
  713. package/dist/core/ResponsePipeline.js.map +0 -1
  714. package/dist/core/SessionFinalizer.d.ts +0 -34
  715. package/dist/core/SessionFinalizer.d.ts.map +0 -1
  716. package/dist/core/SessionFinalizer.js +0 -84
  717. package/dist/core/SessionFinalizer.js.map +0 -1
  718. package/dist/core/SessionManager.d.ts +0 -112
  719. package/dist/core/SessionManager.d.ts.map +0 -1
  720. package/dist/core/SessionManager.js +0 -301
  721. package/dist/core/SessionManager.js.map +0 -1
  722. package/dist/core/SignalCoordinator.d.ts +0 -103
  723. package/dist/core/SignalCoordinator.d.ts.map +0 -1
  724. package/dist/core/SignalCoordinator.js +0 -203
  725. package/dist/core/SignalCoordinator.js.map +0 -1
  726. package/dist/core/SignalEvaluator.d.ts +0 -86
  727. package/dist/core/SignalEvaluator.d.ts.map +0 -1
  728. package/dist/core/SignalEvaluator.js +0 -312
  729. package/dist/core/SignalEvaluator.js.map +0 -1
  730. package/dist/core/SignalProcessor.d.ts +0 -152
  731. package/dist/core/SignalProcessor.d.ts.map +0 -1
  732. package/dist/core/SignalProcessor.js +0 -498
  733. package/dist/core/SignalProcessor.js.map +0 -1
  734. package/dist/core/Step.d.ts +0 -184
  735. package/dist/core/Step.d.ts.map +0 -1
  736. package/dist/core/Step.js +0 -594
  737. package/dist/core/Step.js.map +0 -1
  738. package/dist/core/StepLifecycle.d.ts +0 -43
  739. package/dist/core/StepLifecycle.d.ts.map +0 -1
  740. package/dist/core/StepLifecycle.js +0 -176
  741. package/dist/core/StepLifecycle.js.map +0 -1
  742. package/dist/core/StreamingToolExecutor.d.ts +0 -142
  743. package/dist/core/StreamingToolExecutor.d.ts.map +0 -1
  744. package/dist/core/StreamingToolExecutor.js +0 -483
  745. package/dist/core/StreamingToolExecutor.js.map +0 -1
  746. package/dist/core/ToolLoopExecutor.d.ts +0 -133
  747. package/dist/core/ToolLoopExecutor.d.ts.map +0 -1
  748. package/dist/core/ToolLoopExecutor.js +0 -564
  749. package/dist/core/ToolLoopExecutor.js.map +0 -1
  750. package/dist/core/ToolManager.d.ts +0 -250
  751. package/dist/core/ToolManager.d.ts.map +0 -1
  752. package/dist/core/ToolManager.js +0 -1098
  753. package/dist/core/ToolManager.js.map +0 -1
  754. package/dist/core/createAgent.d.ts +0 -35
  755. package/dist/core/createAgent.d.ts.map +0 -1
  756. package/dist/core/createAgent.js +0 -36
  757. package/dist/core/createAgent.js.map +0 -1
  758. package/dist/core/flow-namespace.d.ts +0 -64
  759. package/dist/core/flow-namespace.d.ts.map +0 -1
  760. package/dist/core/flow-namespace.js +0 -179
  761. package/dist/core/flow-namespace.js.map +0 -1
  762. package/dist/core/toolGates.d.ts +0 -24
  763. package/dist/core/toolGates.d.ts.map +0 -1
  764. package/dist/core/toolGates.js +0 -49
  765. package/dist/core/toolGates.js.map +0 -1
  766. package/dist/types/persistence.d.ts +0 -254
  767. package/dist/types/persistence.d.ts.map +0 -1
  768. package/dist/types/persistence.js +0 -6
  769. package/dist/types/persistence.js.map +0 -1
  770. package/dist/types/prompt-cache.d.ts +0 -15
  771. package/dist/types/prompt-cache.d.ts.map +0 -1
  772. package/dist/types/prompt-cache.js +0 -5
  773. package/dist/types/prompt-cache.js.map +0 -1
  774. package/dist/types/signals.d.ts +0 -263
  775. package/dist/types/signals.d.ts.map +0 -1
  776. package/dist/types/signals.js +0 -10
  777. package/dist/types/signals.js.map +0 -1
  778. package/dist/types/template.d.ts +0 -84
  779. package/dist/types/template.d.ts.map +0 -1
  780. package/dist/types/template.js +0 -2
  781. package/dist/types/template.js.map +0 -1
  782. package/dist/utils/condition.d.ts +0 -63
  783. package/dist/utils/condition.d.ts.map +0 -1
  784. package/dist/utils/condition.js +0 -230
  785. package/dist/utils/condition.js.map +0 -1
  786. package/dist/utils/event.d.ts +0 -6
  787. package/dist/utils/event.d.ts.map +0 -1
  788. package/dist/utils/event.js +0 -17
  789. package/dist/utils/event.js.map +0 -1
  790. package/dist/utils/id.d.ts +0 -33
  791. package/dist/utils/id.d.ts.map +0 -1
  792. package/dist/utils/id.js +0 -77
  793. package/dist/utils/id.js.map +0 -1
  794. package/dist/utils/serialize.d.ts +0 -36
  795. package/dist/utils/serialize.d.ts.map +0 -1
  796. package/dist/utils/serialize.js +0 -72
  797. package/dist/utils/serialize.js.map +0 -1
  798. package/dist/utils/session.d.ts +0 -124
  799. package/dist/utils/session.d.ts.map +0 -1
  800. package/dist/utils/session.js +0 -379
  801. package/dist/utils/session.js.map +0 -1
  802. package/docs/concepts/directives.md +0 -369
  803. package/docs/reference/adapters.md +0 -543
  804. package/docs/reference/create-agent.md +0 -216
  805. package/docs/reference/directive.md +0 -242
  806. package/docs/reference/signals.md +0 -368
  807. package/examples/02-data-extraction.ts +0 -90
  808. package/examples/05-branching.ts +0 -140
  809. package/examples/06-flow-control.ts +0 -103
  810. package/examples/08-persistence.ts +0 -98
  811. package/examples/09-signals.ts +0 -144
  812. package/src/adapters/MemoryAdapter.ts +0 -281
  813. package/src/adapters/MongoAdapter.ts +0 -341
  814. package/src/adapters/OpenSearchAdapter.ts +0 -693
  815. package/src/adapters/PostgreSQLAdapter.ts +0 -487
  816. package/src/adapters/PrismaAdapter.ts +0 -617
  817. package/src/adapters/RedisAdapter.ts +0 -439
  818. package/src/adapters/SQLiteAdapter.ts +0 -496
  819. package/src/adapters/index.ts +0 -43
  820. package/src/adapters/sessionRow.ts +0 -57
  821. package/src/constants/index.ts +0 -2
  822. package/src/core/AutoChainExecutor.ts +0 -397
  823. package/src/core/BranchEvaluator.ts +0 -161
  824. package/src/core/DirectiveChainTracker.ts +0 -144
  825. package/src/core/Events.ts +0 -164
  826. package/src/core/Flow.ts +0 -665
  827. package/src/core/FlowRouter.ts +0 -1540
  828. package/src/core/PersistenceManager.ts +0 -446
  829. package/src/core/PromptComposer.ts +0 -448
  830. package/src/core/PromptSectionCache.ts +0 -125
  831. package/src/core/ResponseEngine.ts +0 -338
  832. package/src/core/ResponseGenerationError.ts +0 -53
  833. package/src/core/ResponseModal.ts +0 -1902
  834. package/src/core/ResponsePipeline.ts +0 -1404
  835. package/src/core/SessionFinalizer.ts +0 -108
  836. package/src/core/SessionManager.ts +0 -372
  837. package/src/core/SignalCoordinator.ts +0 -263
  838. package/src/core/SignalEvaluator.ts +0 -404
  839. package/src/core/SignalProcessor.ts +0 -663
  840. package/src/core/Step.ts +0 -782
  841. package/src/core/StepLifecycle.ts +0 -242
  842. package/src/core/StreamingToolExecutor.ts +0 -609
  843. package/src/core/ToolLoopExecutor.ts +0 -749
  844. package/src/core/ToolManager.ts +0 -1379
  845. package/src/core/createAgent.ts +0 -40
  846. package/src/core/flow-namespace.ts +0 -227
  847. package/src/core/toolGates.ts +0 -72
  848. package/src/types/persistence.ts +0 -303
  849. package/src/types/prompt-cache.ts +0 -17
  850. package/src/types/signals.ts +0 -338
  851. package/src/types/template.ts +0 -98
  852. package/src/utils/condition.ts +0 -296
  853. package/src/utils/event.ts +0 -16
  854. package/src/utils/id.ts +0 -91
  855. package/src/utils/serialize.ts +0 -86
  856. package/src/utils/session.ts +0 -501
@@ -1,1404 +0,0 @@
1
- /**
2
- * Response processing utilities shared between respond() and respondStream() methods
3
- */
4
-
5
- import type { EndedFlow,
6
- AgentOptions,
7
- Event,
8
- SessionState,
9
- Directive,
10
- StructuredSchema,
11
- } from "../types/index.js";
12
- import type { SignalFiring } from "../types/signals.js";
13
- import {
14
- createSession,
15
- enterStep,
16
- mergeCollected,
17
- dropUndeclaredFields,
18
- logger,
19
- historyToEvents,
20
- eventsToHistory,
21
- getLastMessageFromHistory,
22
- } from "../utils/index.js";
23
- import { enterFlow } from "../utils/session.js";
24
- import { createTemplateContext } from "../utils/template.js";
25
- import { Flow } from "./Flow.js";
26
- import { Step, FlowConfigurationError } from "../core/Step.js";
27
- import { FlowRouter } from "./FlowRouter.js";
28
- import { evaluateBranches, createAiConditionEvaluator } from "./BranchEvaluator.js";
29
- import { DirectiveChainTracker } from "./DirectiveChainTracker.js";
30
- import { ResponseGenerationError } from "./ResponseGenerationError.js";
31
- import type { SignalCoordinator } from "./SignalCoordinator.js";
32
-
33
- /**
34
- * Position fields on a Directive that represent a navigation decision.
35
- * When any of these is set, the directive "wins" the turn's position decision
36
- * and downstream resolution (branches, linear, AI) must not run.
37
- */
38
- const DIRECTIVE_POSITION_FIELDS = ['goTo', 'goToStep', 'complete', 'abort', 'reset'] as const;
39
-
40
- /**
41
- * Returns `true` if the given directive has at least one position field set.
42
- * Used as the guard at the directive-bus / branch-evaluation seam:
43
- * if the bus produced a winner with a position field, `evaluateStepBranches`
44
- * (and linear/AI selection) must not run.
45
- */
46
- export function hasDirectivePositionField<TContext = unknown, TData = unknown>(
47
- directive: Directive<TContext, TData> | undefined | null,
48
- ): boolean {
49
- if (!directive) return false;
50
- return DIRECTIVE_POSITION_FIELDS.some(
51
- (field) => (directive as Record<string, unknown>)[field] !== undefined,
52
- );
53
- }
54
-
55
- /**
56
- * Compute the per-key data writes the signal phase applied to a session.
57
- * The signal phase and routing start from the same input session, so any
58
- * key whose value differs between the input and the signal output was
59
- * written by the signal phase alone (handler `updateData` calls).
60
- */
61
- function diffSignalDataWrites<TData>(
62
- before: Partial<TData> | undefined,
63
- after: Partial<TData> | undefined,
64
- ): Partial<TData> {
65
- const beforeData = (before ?? {}) as Record<string, unknown>;
66
- const afterData = (after ?? {}) as Record<string, unknown>;
67
- const writes: Record<string, unknown> = {};
68
- for (const key of Object.keys(afterData)) {
69
- if (afterData[key] !== beforeData[key]) {
70
- writes[key] = afterData[key];
71
- }
72
- }
73
- return writes as Partial<TData>;
74
- }
75
-
76
- export interface ResponsePreparationResult<TContext, TData = unknown> {
77
- effectiveContext: TContext;
78
- session: SessionState<TData>;
79
- /** Context returned by the beforeRespond hook, for the caller to sync back to the agent. */
80
- contextAfterHook?: TContext;
81
- }
82
-
83
- export interface RoutingResult<TContext, TData = unknown> {
84
- selectedFlow: Flow<TContext, TData> | undefined;
85
- selectedStep: Step<TContext, TData> | undefined;
86
- responseDirectives: string[] | undefined;
87
- session: SessionState<TData>;
88
- isFlowComplete: boolean;
89
- completedFlows?: Flow<TContext, TData>[];
90
- /** Flows exited via pendingDirective redirects/resets this turn. */
91
- endedFlows?: EndedFlow[];
92
- }
93
-
94
- /**
95
- * Shared response processing logic between respond() and respondStream() methods
96
- */
97
- export class ResponsePipeline<TContext = unknown, TData = unknown> {
98
- /**
99
- * Per-turn directive chain tracker. Created fresh at the start of each turn
100
- * via `createChainTracker()`. Used by directive application points to detect
101
- * infinite redirection loops.
102
- */
103
- private _chainTracker: DirectiveChainTracker | undefined;
104
-
105
- constructor(
106
- private readonly options: AgentOptions<TContext, TData>,
107
- private readonly getFlows: () => Flow<TContext, TData>[],
108
- private readonly flowRouter: FlowRouter<TContext, TData>,
109
- private readonly signalCoordinator: SignalCoordinator<TContext, TData>,
110
- private readonly updateCollectedData: (updates: Partial<TData>) => Promise<void>,
111
- private readonly getSchema: () => StructuredSchema | undefined
112
- ) { }
113
-
114
- /**
115
- * Create a fresh chain tracker for the current turn.
116
- * Call at the start of each turn; the tracker is discarded at turn end.
117
- */
118
- createChainTracker(): DirectiveChainTracker {
119
- const maxChain = this.options.maxDirectiveChain ?? 10;
120
- this._chainTracker = new DirectiveChainTracker(maxChain);
121
- return this._chainTracker;
122
- }
123
-
124
- /**
125
- * Get the current turn's chain tracker (creates one if none exists).
126
- */
127
- get chainTracker(): DirectiveChainTracker {
128
- if (!this._chainTracker) {
129
- return this.createChainTracker();
130
- }
131
- return this._chainTracker;
132
- }
133
-
134
- /**
135
- * Prepare context and session for response generation
136
- */
137
- async prepareResponseContext(params: {
138
- contextOverride?: Partial<TContext>;
139
- session?: SessionState<TData>;
140
- /** The agent's current context, resolved by the caller (contextProvider already applied). */
141
- currentContext?: TContext;
142
- /** The agent's live session, used when no explicit session is passed. */
143
- currentSession?: SessionState<TData>;
144
- }): Promise<ResponsePreparationResult<TContext, TData>> {
145
- const { contextOverride, session } = params;
146
-
147
- let currentContext = params.currentContext;
148
- let contextAfterHook: TContext | undefined;
149
-
150
- // Call beforeRespond hook if configured
151
- if (this.options.hooks?.beforeRespond && currentContext !== undefined) {
152
- currentContext = await this.options.hooks.beforeRespond(currentContext);
153
- // Surface the hook result so the caller can sync it back to the agent
154
- contextAfterHook = currentContext;
155
- }
156
-
157
- // Merge context with override
158
- const effectiveContext = {
159
- ...(currentContext as Record<string, unknown>),
160
- ...(contextOverride as Record<string, unknown>),
161
- } as TContext;
162
-
163
- // Initialize or get session (use the live session if available)
164
- const targetSession = session || params.currentSession || createSession<TData>();
165
-
166
- return {
167
- effectiveContext,
168
- session: targetSession,
169
- contextAfterHook,
170
- };
171
- }
172
-
173
- /**
174
- * Handle routing and step selection logic
175
- */
176
- async handleRoutingAndStepSelection(params: {
177
- session: SessionState<TData>;
178
- history: Event[];
179
- context: TContext;
180
- signal?: AbortSignal;
181
- /** Restrict ROUTING candidates; directive targets still use the full registry. */
182
- allowedFlows?: string[];
183
- }): Promise<RoutingResult<TContext, TData>> {
184
- const { session, history, context, signal, allowedFlows } = params;
185
-
186
- // PHASE 2: ROUTING + STEP SELECTION - Determine which flow and step to use (combined)
187
- let selectedFlow: Flow<TContext, TData> | undefined;
188
- let responseDirectives: string[] | undefined;
189
- let selectedStep: Step<TContext, TData> | undefined;
190
- let isFlowComplete = false;
191
- let completedFlows: Flow<TContext, TData>[] = [];
192
- let targetSession = session;
193
- // Flows exited this turn via pendingDirective redirect/reset
194
- const endedFlows: EndedFlow[] = [];
195
- let exitPrevFlowId: string | undefined;
196
- let exitPrevFlowTitle: string | undefined;
197
- let exitReason: EndedFlow['reason'] | undefined;
198
-
199
- // Get flows early since we need them for pending directives
200
- const flows = this.getFlows();
201
- // Routing candidates may be narrowed for this turn (entry pins); the
202
- // pendingDirective applier below still resolves targets against the FULL
203
- // registry so declared redirects always resolve.
204
- const routingFlows =
205
- allowedFlows && allowedFlows.length > 0
206
- ? flows.filter(
207
- (f) => allowedFlows.includes(f.id) || allowedFlows.includes(f.title),
208
- )
209
- : flows;
210
-
211
- // Check for pending directive from previous flow completion or external dispatch
212
- if (targetSession.pendingDirective) {
213
- const directive = targetSession.pendingDirective;
214
- logger.debug(
215
- `[ResponsePipeline] Applying pending directive at start of turn`
216
- );
217
-
218
- // Track directive chain depth (Requirement 22.1)
219
- const tracker = this.chainTracker;
220
- tracker.record(directive, "pending");
221
- // If abort (chain breaker), the tracker stops counting; apply normally below
222
-
223
- // Clear pendingDirective before application (unless complete.next chains another)
224
- let nextDirective: typeof targetSession.pendingDirective | undefined = undefined;
225
- if (
226
- directive.complete &&
227
- typeof directive.complete === 'object' &&
228
- directive.complete.next
229
- ) {
230
- nextDirective = directive.complete.next;
231
- }
232
-
233
- targetSession = {
234
- ...targetSession,
235
- pendingDirective: nextDirective,
236
- };
237
-
238
- // Capture the flow we're leaving so directive-driven exits surface on
239
- // the response (endedFlows).
240
- exitPrevFlowId = targetSession.currentFlow?.id;
241
- exitPrevFlowTitle = exitPrevFlowId
242
- ? flows.find((f) => f.id === exitPrevFlowId)?.title
243
- : undefined;
244
-
245
- // Apply the directive: resolve position field to a flow/step
246
- if (directive.goTo) {
247
- exitReason = 'goto';
248
- const flowTarget = typeof directive.goTo === 'string'
249
- ? directive.goTo
250
- : directive.goTo.flow;
251
-
252
- if (flowTarget) {
253
- const targetFlow = flows.find(
254
- (r) => r.id === flowTarget || r.title === flowTarget
255
- );
256
-
257
- if (targetFlow) {
258
- logger.debug(
259
- `[ResponsePipeline] Pending directive goTo → flow: ${targetFlow.title}`
260
- );
261
- targetSession = enterFlow(
262
- targetSession,
263
- targetFlow.id,
264
- targetFlow.title
265
- );
266
-
267
- // Merge initial data if available
268
- if (targetFlow.initialData) {
269
- targetSession = mergeCollected(
270
- targetSession,
271
- targetFlow.initialData
272
- );
273
- }
274
-
275
- // Merge directive-carried data if present
276
- if (typeof directive.goTo === 'object' && directive.goTo.data) {
277
- targetSession = mergeCollected(
278
- targetSession,
279
- directive.goTo.data
280
- );
281
- }
282
-
283
- selectedFlow = targetFlow;
284
-
285
- // If goTo specifies a step, enter it
286
- if (typeof directive.goTo === 'object' && directive.goTo.step) {
287
- const stepTarget = directive.goTo.step;
288
- const targetStep = targetFlow.getStep(stepTarget);
289
- if (targetStep) {
290
- targetSession = enterStep(targetSession, targetStep.id, targetStep.description);
291
- selectedStep = targetStep;
292
- }
293
- }
294
- } else {
295
- logger.warn(
296
- `[FlowConfigurationError] Pending directive goTo target not found: flow "${flowTarget}" does not exist. Falling back to normal routing. Fix the goTo reference or remove the pending directive.`
297
- );
298
- }
299
- }
300
- } else if (directive.goToStep) {
301
- const stepTarget = typeof directive.goToStep === 'string'
302
- ? directive.goToStep
303
- : directive.goToStep.step;
304
- const flowTarget = typeof directive.goToStep === 'object'
305
- ? directive.goToStep.flow
306
- : undefined;
307
-
308
- if (flowTarget) {
309
- const targetFlow = flows.find(
310
- (r) => r.id === flowTarget || r.title === flowTarget
311
- );
312
- if (targetFlow) {
313
- targetSession = enterFlow(targetSession, targetFlow.id, targetFlow.title);
314
- selectedFlow = targetFlow;
315
- const targetStep = targetFlow.getStep(stepTarget);
316
- if (targetStep) {
317
- targetSession = enterStep(targetSession, targetStep.id, targetStep.description);
318
- selectedStep = targetStep;
319
- }
320
- }
321
- } else if (targetSession.currentFlow) {
322
- // Step within current flow
323
- const currentFlow = flows.find(r => r.id === targetSession.currentFlow?.id);
324
- if (currentFlow) {
325
- selectedFlow = currentFlow;
326
- const targetStep = currentFlow.getStep(stepTarget);
327
- if (targetStep) {
328
- targetSession = enterStep(targetSession, targetStep.id, targetStep.description);
329
- selectedStep = targetStep;
330
- }
331
- }
332
- }
333
- } else if (directive.reset) {
334
- exitReason = 'reset';
335
- // Reset current flow
336
- if (targetSession.currentFlow) {
337
- const currentFlow = flows.find(r => r.id === targetSession.currentFlow?.id);
338
- if (currentFlow) {
339
- const resetStep = typeof directive.reset === 'object' && directive.reset.step
340
- ? directive.reset.step
341
- : undefined;
342
- selectedFlow = currentFlow;
343
- if (resetStep) {
344
- const targetStep = currentFlow.getStep(resetStep);
345
- if (targetStep) {
346
- targetSession = enterStep(targetSession, targetStep.id, targetStep.description);
347
- selectedStep = targetStep;
348
- }
349
- } else {
350
- // Reset to initial step
351
- const initialStep = currentFlow.initialStep;
352
- targetSession = enterStep(targetSession, initialStep.id, initialStep.description);
353
- selectedStep = initialStep;
354
- }
355
- }
356
- }
357
- }
358
- // For complete/abort, selectedFlow stays undefined → handled downstream
359
-
360
- // Apply state writes from the directive
361
- if (directive.dataUpdate) {
362
- targetSession = mergeCollected(targetSession, directive.dataUpdate);
363
- }
364
-
365
- // Skip FlowRouter.decideFlowAndStep — the directive resolved the position
366
- }
367
-
368
- if (
369
- exitReason &&
370
- exitPrevFlowId &&
371
- (!selectedFlow || selectedFlow.id !== exitPrevFlowId)
372
- ) {
373
- endedFlows.push({ flowId: exitPrevFlowId, title: exitPrevFlowTitle, reason: exitReason });
374
- }
375
-
376
- // If no pending transition or transition handled, do normal routing.
377
- // Candidates honor allowedFlows; completion scoring runs over the same set.
378
- if (routingFlows.length > 0 && !selectedFlow) {
379
- const orchestration = await this.flowRouter.decideFlowAndStep({
380
- flows: routingFlows,
381
- session: targetSession,
382
- history,
383
- agentOptions: this.options,
384
- provider: this.options.provider,
385
- context,
386
- signal,
387
- });
388
-
389
- selectedFlow = orchestration.selectedFlow;
390
- selectedStep = orchestration.selectedStep;
391
- responseDirectives = orchestration.responseDirectives;
392
- targetSession = orchestration.session;
393
- isFlowComplete = orchestration.isFlowComplete || false;
394
- completedFlows = orchestration.completedFlows || [];
395
-
396
- // Log if flow is complete
397
- if (isFlowComplete) {
398
- logger.debug(
399
- `[ResponsePipeline] Flow complete: all required data collected or last step reached`
400
- );
401
- }
402
- }
403
-
404
- return {
405
- selectedFlow,
406
- selectedStep,
407
- responseDirectives,
408
- session: targetSession,
409
- isFlowComplete,
410
- completedFlows,
411
- ...(endedFlows.length > 0 ? { endedFlows } : {}),
412
- };
413
- }
414
-
415
- /**
416
- * Combine a routing-derived session with the signal phase's non-position
417
- * mutations. The routing session is the base — it owns position state AND
418
- * data merges such as flow initialData and directive-carried writes; the
419
- * signal phase's mutations (trigger recording under `signals`, handler
420
- * data writes) are re-applied on top. Both phases start from the same
421
- * input session, so their mutations cannot silently collide.
422
- */
423
- private combineRoutingAndSignalSessions(params: {
424
- inputSession: SessionState<TData>;
425
- signalSession: SessionState<TData>;
426
- routingSession: SessionState<TData>;
427
- }): SessionState<TData> {
428
- const { inputSession, signalSession, routingSession } = params;
429
- let combined = routingSession;
430
-
431
- if (signalSession.signals !== inputSession.signals) {
432
- combined = { ...combined, signals: signalSession.signals };
433
- }
434
-
435
- const signalWrites = diffSignalDataWrites(
436
- inputSession.data,
437
- signalSession.data,
438
- );
439
- if (Object.keys(signalWrites).length > 0) {
440
- combined = mergeCollected(combined, signalWrites);
441
- }
442
-
443
- return combined;
444
- }
445
-
446
- /**
447
- * Determine next step and update session
448
- */
449
- async determineNextStep(params: {
450
- selectedFlow: Flow<TContext, TData> | undefined;
451
- selectedStep: Step<TContext, TData> | undefined;
452
- session: SessionState<TData>;
453
- isFlowComplete: boolean;
454
- /** The turn's effective context, passed explicitly (no stored pipeline state). */
455
- context: TContext;
456
- /** Merged directive from the directive bus (pre-LLM + post-LLM phases). */
457
- busDirective?: Directive<TContext, TData>;
458
- }): Promise<{
459
- nextStep: Step<TContext, TData> | undefined;
460
- session: SessionState<TData>;
461
- flowChanged?: Flow<TContext, TData>;
462
- /**
463
- * The turn's authoritative completion verdict — callers must use this, not
464
- * the `isFlowComplete` they passed in. The router derives its verdict from
465
- * the LINEAR chain alone (the current step has no successor), before
466
- * branches run, so what it returns is only a proposal. Branches win over
467
- * flow completion (Algorithm 1, STEP 1) and this is where that resolves.
468
- */
469
- isFlowComplete: boolean;
470
- }> {
471
- const { selectedFlow, selectedStep, session, isFlowComplete, context, busDirective } = params;
472
-
473
- if (!selectedFlow) {
474
- return { nextStep: undefined, session, isFlowComplete };
475
- }
476
-
477
- // ─── GUARD: directive bus winner with a position field preempts branches ───
478
- // Resolution precedence (design.md): bus > branches > linear > AI.
479
- // If the bus produced a directive with a position field (goTo, goToStep,
480
- // complete, abort, reset), that decision wins the turn. Do NOT evaluate
481
- // branches or linear/AI selection — the caller applies the bus directive.
482
- if (hasDirectivePositionField(busDirective)) {
483
- logger.debug(
484
- `[ResponsePipeline] Directive bus winner has position field — skipping branch evaluation and linear/AI selection`,
485
- );
486
- return { nextStep: undefined, session, isFlowComplete };
487
- }
488
-
489
- // STEP 1 (Algorithm 1): branches win over linear chain AND flow completion.
490
- // Evaluate branches before checking isFlowComplete — a branch can redirect
491
- // even from the "last" step (which getCandidateStepsWithConditions marks as complete).
492
- if (!selectedStep) {
493
- const currentStep = session.currentFlow?.id === selectedFlow.id && session.currentStep
494
- ? selectedFlow.getStep(session.currentStep.id)
495
- : undefined;
496
-
497
- if (currentStep?.branches && currentStep.branches.length > 0) {
498
- const branchResult = await this.evaluateStepBranches(
499
- currentStep, selectedFlow, session, context
500
- );
501
- if (branchResult) {
502
- // A branch that resolved a position overrides the router's completion
503
- // proposal. Without this, a branch that parks on a step of its own
504
- // flow (`then: '<stepId>'`, `then: { reset: true }`) keeps
505
- // isFlowComplete=true, the caller drops the step it just resolved,
506
- // and the turn ends as a silent completion — no LLM call, empty
507
- // reply, flow marked completed for the rest of the session.
508
- const resolvedPosition = Boolean(branchResult.nextStep || branchResult.flowChanged);
509
- return { ...branchResult, isFlowComplete: resolvedPosition ? false : isFlowComplete };
510
- }
511
- // undefined → fall through to linear/AI selection or flow completion
512
- }
513
- }
514
-
515
- if (isFlowComplete) {
516
- return { nextStep: undefined, session, isFlowComplete: true };
517
- }
518
-
519
- let nextStep: Step<TContext, TData>;
520
-
521
- // If we have a selected step from the combined routing decision, use it
522
- if (selectedStep) {
523
- nextStep = selectedStep;
524
- } else {
525
- // Determine current step from session if we're already in this flow
526
- const currentStep = session.currentFlow?.id === selectedFlow.id && session.currentStep
527
- ? selectedFlow.getStep(session.currentStep.id)
528
- : undefined;
529
-
530
- // Get candidate steps based on current position in the flow
531
- const candidates = await this.flowRouter.getCandidateStepsWithConditions(
532
- selectedFlow,
533
- currentStep, // Pass current step instead of undefined to maintain progression
534
- createTemplateContext({ data: session.data, session, context })
535
- );
536
-
537
- if (candidates.length > 0) {
538
- nextStep = candidates[0].step;
539
- logger.debug(
540
- `[ResponsePipeline] Using first valid step: ${nextStep.id}${currentStep ? ' (progressing from ' + currentStep.id + ')' : ' for new flow'}`
541
- );
542
- } else {
543
- // Fallback to initial step even if it should be skipped
544
- nextStep = selectedFlow.initialStep;
545
- logger.warn(
546
- `[FlowConfigurationError] No valid steps found in flow "${selectedFlow.title}": all candidates were skipped. Falling back to initial step "${nextStep.id}". Review step skip conditions.`
547
- );
548
- }
549
- }
550
-
551
- // Before entering the step, check if requires fields are satisfied
552
- if (nextStep.requires && nextStep.requires.length > 0) {
553
- const sessionData = session.data || {};
554
- const missingRequires = nextStep.requires.filter(
555
- field => (sessionData as Record<string, unknown>)[String(field)] === undefined
556
- );
557
- if (missingRequires.length > 0) {
558
- logger.debug(
559
- `[ResponsePipeline] Cannot enter step "${nextStep.id}": missing required fields [${missingRequires.join(', ')}]. Staying at current step.`
560
- );
561
- // Stay at current step - don't enter the next one
562
- const currentStepId = session.currentStep?.id;
563
- if (currentStepId && selectedFlow) {
564
- const currentStepInstance = selectedFlow.getStep(currentStepId);
565
- if (currentStepInstance) {
566
- nextStep = currentStepInstance;
567
- }
568
- }
569
- return { nextStep, session, isFlowComplete: false };
570
- }
571
- }
572
-
573
- // Update session with next step
574
- const updatedSession = enterStep(
575
- session,
576
- nextStep.id,
577
- nextStep.description
578
- );
579
- logger.debug(`[ResponsePipeline] Entered step: ${nextStep.id}`);
580
-
581
- return { nextStep, session: updatedSession, isFlowComplete: false };
582
- }
583
-
584
- /**
585
- * Evaluate branches on a step and resolve the `then` value.
586
- *
587
- * Resolution rules (Algorithm 1, STEP 1 + then resolution):
588
- * - String `then` → look up in current flow's step registry first (local step wins).
589
- * - Not a local step → look up in agent's flow registry; if found, treat as goTo directive.
590
- * - Neither → throw FlowConfigurationError.
591
- * - Directive `then` → apply directly (bypass directive bus merge).
592
- *
593
- * Returns the resolved { nextStep, session, flowChanged? } or undefined if no branch matched.
594
- */
595
- async evaluateStepBranches(
596
- currentStep: Step<TContext, TData>,
597
- selectedFlow: Flow<TContext, TData>,
598
- session: SessionState<TData>,
599
- context: TContext,
600
- ): Promise<{ nextStep: Step<TContext, TData> | undefined; session: SessionState<TData>; flowChanged?: Flow<TContext, TData> } | undefined> {
601
- const history = session.history ? historyToEvents(session.history) : [];
602
-
603
- // Build the BranchPredicateContext
604
- const branchCtx = {
605
- data: session.data,
606
- context,
607
- session,
608
- history,
609
- };
610
-
611
- // Create the AI condition evaluator
612
- const aiEvaluator = createAiConditionEvaluator(
613
- this.options.provider,
614
- history,
615
- context,
616
- );
617
-
618
- const result = await evaluateBranches(
619
- currentStep.branches!,
620
- branchCtx,
621
- aiEvaluator,
622
- );
623
-
624
- if (result === undefined) {
625
- return undefined; // No branch matched → fall through to linear/AI selection
626
- }
627
-
628
- // Task 4.3: Directive `then` value — apply directly
629
- if (typeof result === 'object' && result !== null) {
630
- const directive = result;
631
- return this.applyBranchDirective(directive, selectedFlow, session);
632
- }
633
-
634
- // Task 4.2: String `then` resolution
635
- // 1. Look up in current flow's step registry first (local step wins)
636
- const localStep = selectedFlow.getStep(result);
637
- if (localStep) {
638
- const updatedSession = enterStep(session, localStep.id, localStep.description);
639
- logger.debug(`[ResponsePipeline] Branch resolved to local step: ${localStep.id}`);
640
- return { nextStep: localStep, session: updatedSession };
641
- }
642
-
643
- // 2. Look up in agent's flow registry
644
- const flows = this.getFlows();
645
- const targetFlow = flows.find(f => f.id === result || f.title === result);
646
- if (targetFlow) {
647
- // Treat as applyDirective({ goTo: result }) — enter the target flow
648
- logger.debug(`[ResponsePipeline] Branch resolved to flow: ${targetFlow.title}`);
649
- const updatedSession = enterFlow(session, targetFlow.id, targetFlow.title);
650
- return { nextStep: undefined, session: updatedSession, flowChanged: targetFlow };
651
- }
652
-
653
- // 3. Neither → throw FlowConfigurationError
654
- throw new FlowConfigurationError(
655
- `[FlowConfigurationError] Unresolved branch target: "${result}" does not match any step in flow "${selectedFlow.id}" or any flow in the agent. ` +
656
- `Source: ${selectedFlow.id}.${currentStep.id}. Fix the branch "then" value to reference a valid step id or flow id/title.`
657
- );
658
- }
659
-
660
- /**
661
- * Apply a Directive returned by a branch entry's `then` value.
662
- * Branches bypass the directive bus — the Directive is the position decision.
663
- */
664
- private applyBranchDirective(
665
- directive: Directive<TContext, TData>,
666
- selectedFlow: Flow<TContext, TData>,
667
- session: SessionState<TData>,
668
- ): { nextStep: Step<TContext, TData> | undefined; session: SessionState<TData>; flowChanged?: Flow<TContext, TData> } {
669
- // Track directive chain depth (Requirement 22.1)
670
- const tracker = this.chainTracker;
671
- tracker.record(directive, `branch:${selectedFlow.id}`);
672
-
673
- let updatedSession = session;
674
-
675
- // Apply state writes first
676
- if (directive.dataUpdate) {
677
- updatedSession = mergeCollected(updatedSession, directive.dataUpdate);
678
- }
679
-
680
- // Handle position fields
681
- if (directive.goToStep) {
682
- const stepTarget = typeof directive.goToStep === 'string'
683
- ? directive.goToStep
684
- : directive.goToStep.step;
685
- const flowTarget = typeof directive.goToStep === 'object'
686
- ? directive.goToStep.flow
687
- : undefined;
688
-
689
- if (flowTarget) {
690
- // Cross-flow step reference — enter the target flow first
691
- const flows = this.getFlows();
692
- const targetFlow = flows.find(f => f.id === flowTarget || f.title === flowTarget);
693
- if (targetFlow) {
694
- updatedSession = enterFlow(updatedSession, targetFlow.id, targetFlow.title);
695
- updatedSession = enterStep(updatedSession, stepTarget);
696
- // Try to resolve the target step instance for the caller
697
- const targetStepInstance = targetFlow.getStep(stepTarget);
698
- logger.debug(`[ResponsePipeline] Branch directive goToStep → ${flowTarget}.${stepTarget}`);
699
- return { nextStep: targetStepInstance || undefined, session: updatedSession, flowChanged: targetFlow };
700
- }
701
- throw new FlowConfigurationError(
702
- `[FlowConfigurationError] Branch directive goToStep targets unknown flow: "${flowTarget}" does not match any flow id or title. ` +
703
- `Fix the goToStep.flow value or use goTo to target a known flow.`
704
- );
705
- }
706
-
707
- // Local step reference
708
- const targetStep = selectedFlow.getStep(stepTarget);
709
- if (targetStep) {
710
- updatedSession = enterStep(updatedSession, targetStep.id, targetStep.description);
711
- logger.debug(`[ResponsePipeline] Branch directive goToStep → ${targetStep.id}`);
712
- return { nextStep: targetStep, session: updatedSession };
713
- }
714
- throw new FlowConfigurationError(
715
- `[FlowConfigurationError] Branch directive goToStep targets unknown step: "${stepTarget}" does not exist in flow "${selectedFlow.id}". ` +
716
- `Fix the goToStep value to reference a valid step id in the current flow.`
717
- );
718
- }
719
-
720
- if (directive.goTo) {
721
- const flowTarget = typeof directive.goTo === 'string'
722
- ? directive.goTo
723
- : directive.goTo.flow ?? directive.goTo.step;
724
-
725
- if (flowTarget) {
726
- const flows = this.getFlows();
727
- const targetFlow = flows.find(f => f.id === flowTarget || f.title === flowTarget);
728
- if (targetFlow) {
729
- updatedSession = enterFlow(updatedSession, targetFlow.id, targetFlow.title);
730
-
731
- // If goTo is an object with a step field, enter that step too
732
- if (typeof directive.goTo === 'object' && directive.goTo.step) {
733
- updatedSession = enterStep(updatedSession, directive.goTo.step);
734
- }
735
-
736
- logger.debug(`[ResponsePipeline] Branch directive goTo → ${targetFlow.title}`);
737
- return { nextStep: undefined, session: updatedSession, flowChanged: targetFlow };
738
- }
739
- throw new FlowConfigurationError(
740
- `[FlowConfigurationError] Branch directive goTo targets unknown flow: "${flowTarget}" does not match any flow id or title. ` +
741
- `Fix the goTo value to reference a valid flow.`
742
- );
743
- }
744
- }
745
-
746
- if (directive.complete) {
747
- logger.debug(`[ResponsePipeline] Branch directive complete`);
748
- return { nextStep: undefined, session: updatedSession };
749
- }
750
-
751
- if (directive.abort) {
752
- logger.debug(`[ResponsePipeline] Branch directive abort`);
753
- return { nextStep: undefined, session: updatedSession };
754
- }
755
-
756
- if (directive.reset) {
757
- const resetStep = typeof directive.reset === 'object' && directive.reset.step
758
- ? directive.reset.step
759
- : undefined;
760
- if (resetStep) {
761
- const targetStep = selectedFlow.getStep(resetStep);
762
- if (targetStep) {
763
- updatedSession = enterStep(updatedSession, targetStep.id, targetStep.description);
764
- return { nextStep: targetStep, session: updatedSession };
765
- }
766
- }
767
- // Reset to initial step
768
- const initialStep = selectedFlow.initialStep;
769
- updatedSession = enterStep(updatedSession, initialStep.id, initialStep.description);
770
- logger.debug(`[ResponsePipeline] Branch directive reset → ${initialStep.id}`);
771
- return { nextStep: initialStep, session: updatedSession };
772
- }
773
-
774
- // Directive with only non-position fields (e.g., just dataUpdate/contextUpdate/reply)
775
- // No position change — fall through to linear selection
776
- return { nextStep: undefined, session: updatedSession };
777
- }
778
-
779
-
780
- /**
781
- * TURN ROUTING + STEP SELECTION — the single entry point for deciding which
782
- * flow and step a turn renders. Runs the routing-skip optimization, the
783
- * pre-signal phase (parallel with routing when a processor is configured),
784
- * pre-extraction, and next-step determination.
785
- */
786
- async routeAndSelectStep(params: {
787
- session: SessionState<TData>;
788
- history: Event[]; // Use Event[] for internal processing
789
- context: TContext;
790
- signal?: AbortSignal;
791
- /** Restrict this turn's routing candidates (entry pins). */
792
- allowedFlows?: string[];
793
- }): Promise<{
794
- selectedFlow?: Flow<TContext, TData>;
795
- selectedStep?: Step<TContext, TData>;
796
- responseDirectives?: string[];
797
- session: SessionState<TData>;
798
- isFlowComplete: boolean;
799
- /** Signal firings from the pre-phase (threaded through for response surface). */
800
- signalFirings?: SignalFiring<TContext, TData>[];
801
- /** Non-position signal directive for pre-LLM augmentation (appendPrompt, injectTools, etc). */
802
- signalPreDirective?: Directive<TContext, TData>;
803
- /** Pre-signal phase halted the turn. */
804
- signalHalted?: boolean;
805
- /** Reply text from the halt directive. */
806
- signalHaltReply?: string;
807
- /** Flows exited via pendingDirective redirects/resets this turn. */
808
- endedFlows?: EndedFlow[];
809
- }> {
810
- try {
811
- // Create a fresh chain tracker for this turn (Requirement 22.1)
812
- this.createChainTracker();
813
-
814
- // ROUTING SKIP OPTIMIZATION (Requirements 20.1, 20.2, 20.3):
815
- // When the current step has collect fields AND step-scoped pre-extraction
816
- // populates at least one of those fields, skip FlowRouter.decideFlowAndStep
817
- // for this turn. The check's extraction result is reused by the fallthrough
818
- // paths below so a turn costs at most ONE extraction LLM call.
819
- const routingSkipCheck = await this.attemptRoutingSkipForCollect(params);
820
- if (routingSkipCheck.skipResult) {
821
- const skipResult = routingSkipCheck.skipResult;
822
- // Even when routing is skipped, run pre-signal phase if processor is present
823
- if (this.signalCoordinator.enabled) {
824
- const signalResult = await this.signalCoordinator.runPrePhase(
825
- params.session, params.context, params.history,
826
- );
827
- // If signal halts, override the routing skip result
828
- if (signalResult.mergedDirective?.halt) {
829
- return {
830
- ...skipResult,
831
- session: signalResult.updatedSession,
832
- signalFirings: signalResult.firings,
833
- signalHalted: true,
834
- signalHaltReply: signalResult.mergedDirective.reply,
835
- };
836
- }
837
- // If signal has position fields, override routing skip result
838
- if (hasDirectivePositionField(signalResult.mergedDirective)) {
839
- return this.signalCoordinator.applyPositionDirective(signalResult);
840
- }
841
- // Non-position directive: keep the skip result (extraction merges,
842
- // step entry) and re-apply the signal phase's non-position mutations
843
- return {
844
- ...skipResult,
845
- session: this.combineRoutingAndSignalSessions({
846
- inputSession: params.session,
847
- signalSession: signalResult.updatedSession,
848
- routingSession: skipResult.session,
849
- }),
850
- signalFirings: signalResult.firings,
851
- signalPreDirective: signalResult.mergedDirective || undefined,
852
- };
853
- }
854
- return skipResult;
855
- }
856
-
857
- // ── PARALLEL PRE-SIGNAL PHASE + ROUTING (Algorithm 5) ────────────────
858
- // When signalProcessor is present, run pre-signals in parallel with routing.
859
- // When absent, call the router directly (zero overhead, preserve current behavior).
860
- if (this.signalCoordinator.enabled) {
861
- // Run pre-signal phase in parallel with routing (Requirement 8.1)
862
- const [signalResult, routingResult] = await Promise.all([
863
- this.signalCoordinator.runPrePhase(
864
- params.session, params.context, params.history,
865
- ),
866
- this.handleRoutingAndStepSelection({
867
- session: params.session,
868
- history: params.history,
869
- context: params.context,
870
- signal: params.signal,
871
- allowedFlows: params.allowedFlows,
872
- }),
873
- ]);
874
-
875
- // ── Requirement 8.2: halt → discard routing, skip LLM ────────────
876
- if (signalResult.mergedDirective?.halt) {
877
- return {
878
- selectedFlow: undefined,
879
- selectedStep: undefined,
880
- session: signalResult.updatedSession,
881
- isFlowComplete: false,
882
- signalFirings: signalResult.firings,
883
- signalHalted: true,
884
- signalHaltReply: signalResult.mergedDirective.reply,
885
- };
886
- }
887
-
888
- // ── Requirement 8.3: position directive → discard routing, apply signal position ──
889
- if (hasDirectivePositionField(signalResult.mergedDirective)) {
890
- return this.signalCoordinator.applyPositionDirective(signalResult);
891
- }
892
-
893
- // ── Requirement 8.4: non-position directive → use routing, propagate augmentation ──
894
- // ── Requirement 8.5: no directive → use routing as-is ─────────────
895
- // Base = routed session (position state AND router data merges such as
896
- // flow initialData survive); the signal phase's non-position mutations
897
- // (trigger state, handler data writes) are re-applied on top.
898
- let updatedSession = this.combineRoutingAndSignalSessions({
899
- inputSession: params.session,
900
- signalSession: signalResult.updatedSession,
901
- routingSession: routingResult.session,
902
- });
903
-
904
- // Explicit dataUpdate from the signal's merged directive lands last
905
- if (signalResult.mergedDirective?.dataUpdate) {
906
- updatedSession = mergeCollected(updatedSession, signalResult.mergedDirective.dataUpdate);
907
- }
908
-
909
- const isFlowComplete = routingResult.isFlowComplete;
910
-
911
- // PRE-EXTRACTION: same logic as below — extract data from user message.
912
- // Reuses the routing-skip check's step-scoped extraction when it already
913
- // ran this turn (at most ONE extraction LLM call per turn).
914
- if (routingResult.selectedFlow && !isFlowComplete) {
915
- let extractedData: Partial<TData> | undefined;
916
- if (routingSkipCheck.extractionRan) {
917
- extractedData = routingSkipCheck.extractedData;
918
- } else if (this.shouldPreExtractData(routingResult.selectedFlow)) {
919
- logger.debug(
920
- `[ResponsePipeline] Pre-extracting data for flow: ${routingResult.selectedFlow.title}`
921
- );
922
- extractedData = await this.preExtractFlowData({
923
- route: routingResult.selectedFlow,
924
- history: params.history,
925
- context: params.context,
926
- session: updatedSession,
927
- signal: params.signal,
928
- });
929
- }
930
- if (extractedData && Object.keys(extractedData).length > 0) {
931
- logger.debug(`[ResponsePipeline] Pre-extracted data:`, extractedData);
932
- updatedSession = mergeCollected(updatedSession, extractedData);
933
- await this.updateCollectedData(extractedData);
934
- }
935
- }
936
-
937
- // Determine next step
938
- const stepResult = await this.determineNextStep({
939
- selectedFlow: routingResult.selectedFlow,
940
- selectedStep: routingResult.selectedStep,
941
- session: updatedSession,
942
- isFlowComplete,
943
- context: params.context,
944
- });
945
-
946
- return {
947
- selectedFlow: stepResult.flowChanged || routingResult.selectedFlow,
948
- selectedStep: stepResult.nextStep,
949
- responseDirectives: routingResult.responseDirectives,
950
- session: stepResult.session,
951
- isFlowComplete: stepResult.isFlowComplete,
952
- signalFirings: signalResult.firings,
953
- signalPreDirective: signalResult.mergedDirective || undefined,
954
- endedFlows: routingResult.endedFlows,
955
- };
956
- }
957
-
958
- // ── No signal processor: existing behavior (zero overhead) ────────────
959
- const routingResult = await this.handleRoutingAndStepSelection({
960
- session: params.session,
961
- history: params.history,
962
- context: params.context,
963
- signal: params.signal,
964
- allowedFlows: params.allowedFlows,
965
- });
966
-
967
- let updatedSession = routingResult.session;
968
- const isFlowComplete = routingResult.isFlowComplete;
969
-
970
- // PRE-EXTRACTION: If entering a flow that collects data, extract data from user message first
971
- // This allows us to skip steps whose data is already provided.
972
- // Reuses the routing-skip check's step-scoped extraction when it already
973
- // ran this turn — at most ONE extraction LLM call per turn.
974
- if (routingResult.selectedFlow && !isFlowComplete) {
975
- // Always pre-extract when flow collects data (not just on new flow entry)
976
- // This ensures step selection has the most up-to-date data
977
- let extractedData: Partial<TData> | undefined;
978
- if (routingSkipCheck.extractionRan) {
979
- extractedData = routingSkipCheck.extractedData;
980
- } else if (this.shouldPreExtractData(routingResult.selectedFlow)) {
981
- logger.debug(
982
- `[ResponsePipeline] Pre-extracting data for flow: ${routingResult.selectedFlow.title}`
983
- );
984
-
985
- extractedData = await this.preExtractFlowData({
986
- route: routingResult.selectedFlow,
987
- history: params.history,
988
- context: params.context,
989
- session: updatedSession,
990
- signal: params.signal,
991
- });
992
- }
993
-
994
- if (extractedData && Object.keys(extractedData).length > 0) {
995
- logger.debug(
996
- `[ResponsePipeline] Pre-extracted data:`,
997
- extractedData
998
- );
999
- // Merge pre-extracted data into session before step selection
1000
- updatedSession = mergeCollected(updatedSession, extractedData);
1001
- // Also update agent's collected data
1002
- await this.updateCollectedData(extractedData);
1003
- }
1004
- }
1005
-
1006
- // Determine next step using pipeline method for consistency
1007
- const stepResult = await this.determineNextStep({
1008
- selectedFlow: routingResult.selectedFlow,
1009
- selectedStep: routingResult.selectedStep,
1010
- session: updatedSession, // Use updated session with pre-extracted data
1011
- isFlowComplete, // Use updated completion status
1012
- context: params.context,
1013
- });
1014
-
1015
- return {
1016
- selectedFlow: stepResult.flowChanged || routingResult.selectedFlow,
1017
- selectedStep: stepResult.nextStep, // Use the determined next step
1018
- responseDirectives: routingResult.responseDirectives,
1019
- session: stepResult.session,
1020
- // determineNextStep owns the verdict: a branch that resolved a position
1021
- // (new flow, or a step of this one) overrides the router's proposal.
1022
- isFlowComplete: stepResult.isFlowComplete,
1023
- endedFlows: routingResult.endedFlows,
1024
- };
1025
- } catch (error) {
1026
- throw ResponseGenerationError.fromError(error, 'routing_optimization', params);
1027
- }
1028
- }
1029
-
1030
- /**
1031
- * RENDER-STEP RESOLUTION — resolve the step a flow response will render,
1032
- * shared by the streaming and non-streaming paths. When no step was
1033
- * pre-selected: branches win over the linear chain, then candidate steps,
1034
- * then the initial-step fallback. Enforces `requires` (stays at the current
1035
- * step when required fields are missing) and enters the resolved step.
1036
- *
1037
- * Returns `flowTransition: true` when a branch resolved to a flow
1038
- * transition or completion — there is no local step to render and the
1039
- * caller handles the transition.
1040
- */
1041
- async resolveRenderStep(params: {
1042
- selectedFlow: Flow<TContext, TData>;
1043
- selectedStep?: Step<TContext, TData>;
1044
- session: SessionState<TData>;
1045
- context: TContext;
1046
- }): Promise<{
1047
- nextStep?: Step<TContext, TData>;
1048
- session: SessionState<TData>;
1049
- flowTransition: boolean;
1050
- }> {
1051
- const { selectedFlow, selectedStep, context } = params;
1052
- let session = params.session;
1053
-
1054
- // Determine next step
1055
- let nextStep: Step<TContext, TData>;
1056
- if (selectedStep) {
1057
- nextStep = selectedStep;
1058
- } else {
1059
- // Determine current step from session if we're already in this flow
1060
- const isInSameFlow = session.currentFlow?.id === selectedFlow.id;
1061
- const currentStep = isInSameFlow && session.currentStep
1062
- ? selectedFlow.getStep(session.currentStep.id)
1063
- : undefined;
1064
-
1065
- logger.debug(`[ResponsePipeline] Step determination: flow match=${isInSameFlow}, currentFlow=${session.currentFlow?.id}, selectedFlow=${selectedFlow.id}, currentStep=${currentStep?.id || 'none'}`);
1066
-
1067
- // STEP 1 (Algorithm 1): branches win over linear chain
1068
- if (currentStep?.branches && currentStep.branches.length > 0) {
1069
- const branchResult = await this.evaluateStepBranches(
1070
- currentStep, selectedFlow, session, context
1071
- );
1072
- if (branchResult) {
1073
- if (branchResult.nextStep) {
1074
- nextStep = branchResult.nextStep;
1075
- session = branchResult.session;
1076
- } else {
1077
- // Flow transition or completion — no local step to render
1078
- return { nextStep: undefined, session: branchResult.session, flowTransition: true };
1079
- }
1080
- }
1081
- }
1082
-
1083
- if (!nextStep!) {
1084
- // Get candidate steps based on current position in the flow
1085
- const candidates = await this.flowRouter.getCandidateStepsWithConditions(
1086
- selectedFlow,
1087
- currentStep, // Pass current step instead of undefined to maintain progression
1088
- createTemplateContext({ data: session.data, session, context })
1089
- );
1090
-
1091
- logger.debug(`[ResponsePipeline] Found ${candidates.length} candidate steps${currentStep ? ' from current step ' + currentStep.id : ' (new flow entry)'}`);
1092
-
1093
- if (candidates.length > 0) {
1094
- nextStep = candidates[0].step;
1095
- logger.debug(`[ResponsePipeline] Using first valid step: ${nextStep.id}${currentStep ? ' (progressing from ' + currentStep.id + ')' : ' for new flow'}`);
1096
- } else {
1097
- // Fallback to initial step even if it should be skipped
1098
- nextStep = selectedFlow.initialStep;
1099
- logger.warn(`[FlowConfigurationError] No valid steps found: all candidates were skipped in flow. Falling back to initial step "${nextStep.id}". Review step skip conditions.`);
1100
- }
1101
- }
1102
- }
1103
-
1104
- // Update session with next step
1105
- // If the next step has requires fields that are missing, stay at the previous step
1106
- if (nextStep.requires && nextStep.requires.length > 0) {
1107
- const sessionData = session.data || {};
1108
- const missingRequires = nextStep.requires.filter(
1109
- field => (sessionData as Record<string, unknown>)[String(field)] === undefined
1110
- );
1111
- if (missingRequires.length > 0) {
1112
- const warning = `[FlowConfigurationError] Cannot advance to step "${nextStep.description || nextStep.id}": ` +
1113
- `missing required fields [${missingRequires.join(', ')}]. Staying at current step. Ensure preceding steps collect these fields.`;
1114
- logger.warn(warning);
1115
- console.warn(warning);
1116
- // Stay at the current step - don't enter the next one
1117
- const currentStepId = session.currentStep?.id;
1118
- if (currentStepId) {
1119
- const currentStepInstance = selectedFlow.getStep(currentStepId);
1120
- if (currentStepInstance) {
1121
- nextStep = currentStepInstance;
1122
- logger.debug(`[ResponsePipeline] Staying at current step: ${nextStep.id} due to missing requires`);
1123
- }
1124
- }
1125
- } else {
1126
- session = enterStep(session, nextStep.id, nextStep.description);
1127
- logger.debug(`[ResponsePipeline] Entered step: ${nextStep.id}`);
1128
- }
1129
- } else {
1130
- session = enterStep(session, nextStep.id, nextStep.description);
1131
- logger.debug(`[ResponsePipeline] Entered step: ${nextStep.id}`);
1132
- }
1133
-
1134
- return { nextStep, session, flowTransition: false };
1135
- }
1136
-
1137
- /**
1138
- * Routing skip check (Requirements 20.1, 20.2, 20.3):
1139
- * When the current step declares `collect` fields AND step-scoped extraction
1140
- * populates at least one of those fields from the user's message, the caller
1141
- * skips routing for this turn (`skipResult`).
1142
- *
1143
- * The extraction is scoped to EXACTLY this step's collect fields — incidental
1144
- * mentions of other (e.g. other-flow) fields cannot pin the routing decision.
1145
- * It is skipped entirely when every collect field is already populated.
1146
- *
1147
- * Always returns what the check did so the caller can reuse the single
1148
- * extraction result in its normal path instead of extracting twice:
1149
- * - `extractionRan` — whether an extraction LLM call was made this turn.
1150
- * - `extractedData` — that call's result (empty when nothing found / not run).
1151
- */
1152
- private async attemptRoutingSkipForCollect(params: {
1153
- session: SessionState<TData>;
1154
- history: Event[];
1155
- context: TContext;
1156
- signal?: AbortSignal;
1157
- }): Promise<{
1158
- extractionRan: boolean;
1159
- extractedData: Partial<TData>;
1160
- skipResult?: {
1161
- selectedFlow?: Flow<TContext, TData>;
1162
- selectedStep?: Step<TContext, TData>;
1163
- responseDirectives?: string[];
1164
- session: SessionState<TData>;
1165
- isFlowComplete: boolean;
1166
- };
1167
- }> {
1168
- const { session } = params;
1169
- const notApplicable = { extractionRan: false, extractedData: {} as Partial<TData> };
1170
-
1171
- // Only applies when we already have a current flow and step
1172
- if (!session.currentFlow || !session.currentStep) {
1173
- return notApplicable;
1174
- }
1175
-
1176
- // Also skip this optimization if there's a pending directive (it takes priority)
1177
- if (session.pendingDirective) {
1178
- return notApplicable;
1179
- }
1180
-
1181
- // Look up the actual Flow and Step objects to access `collect`
1182
- const currentFlow = this.getFlows().find(
1183
- (f) => f.id === session.currentFlow?.id
1184
- );
1185
- if (!currentFlow) {
1186
- return notApplicable;
1187
- }
1188
-
1189
- const currentStep = currentFlow.getStep(session.currentStep.id);
1190
- if (!currentStep || !currentStep.collect || currentStep.collect.length === 0) {
1191
- return notApplicable;
1192
- }
1193
-
1194
- const collectFields = currentStep.collect;
1195
-
1196
- // Every relevant field already set — the skip cannot fire; don't spend
1197
- // an extraction LLM call probing for it.
1198
- const sessionRecord = (session.data ?? {}) as Record<string, unknown>;
1199
- const allPopulated = collectFields.every((field) => {
1200
- const value = sessionRecord[String(field)];
1201
- return value !== undefined && value !== null;
1202
- });
1203
- if (allPopulated) {
1204
- return notApplicable;
1205
- }
1206
-
1207
- // Snapshot current data for comparison
1208
- const dataBefore = { ...sessionRecord } as Record<string, unknown>;
1209
-
1210
- // Step-scoped pre-extraction: see if the user's message populates any of
1211
- // THIS step's collect fields.
1212
- const extractedData = await this.preExtractFlowData({
1213
- route: currentFlow,
1214
- history: params.history,
1215
- context: params.context,
1216
- session,
1217
- signal: params.signal,
1218
- restrictToFields: collectFields.map(String),
1219
- });
1220
-
1221
- if (!extractedData || Object.keys(extractedData).length === 0) {
1222
- return { extractionRan: true, extractedData: {} };
1223
- }
1224
-
1225
- // Determine which collect fields were newly populated by pre-extraction
1226
- const extractedRecord = extractedData as Record<string, unknown>;
1227
- const populatedCollectFields: string[] = [];
1228
- for (const field of collectFields) {
1229
- const key = String(field);
1230
- const hadValue =
1231
- dataBefore[key] !== undefined && dataBefore[key] !== null;
1232
- const hasNewValue =
1233
- extractedRecord[key] !== undefined && extractedRecord[key] !== null;
1234
- if (hasNewValue && !hadValue) {
1235
- populatedCollectFields.push(key);
1236
- }
1237
- }
1238
-
1239
- if (populatedCollectFields.length === 0) {
1240
- // Pre-extraction didn't populate any declared collect field — no skip
1241
- return { extractionRan: true, extractedData };
1242
- }
1243
-
1244
- // ROUTING SKIP: pre-extraction populated collect fields → retain current flow/step
1245
- logger.debug(
1246
- `[ResponsePipeline] Routing skip: pre-extraction populated collect fields [${populatedCollectFields.join(', ')}] for step "${currentStep.id}" — skipping FlowRouter`
1247
- );
1248
-
1249
- // Merge extracted data into session
1250
- const updatedSession = mergeCollected(session, extractedData);
1251
- await this.updateCollectedData(extractedData);
1252
-
1253
- // Determine next step using pipeline method for consistency
1254
- // Pass the current flow/step as the routing result (retained)
1255
- const stepResult = await this.determineNextStep({
1256
- selectedFlow: currentFlow,
1257
- selectedStep: currentStep,
1258
- session: updatedSession,
1259
- isFlowComplete: false,
1260
- context: params.context,
1261
- });
1262
-
1263
- return {
1264
- extractionRan: true,
1265
- extractedData,
1266
- skipResult: {
1267
- selectedFlow: stepResult.flowChanged || currentFlow,
1268
- selectedStep: stepResult.nextStep,
1269
- responseDirectives: undefined,
1270
- session: stepResult.session,
1271
- isFlowComplete: stepResult.isFlowComplete,
1272
- },
1273
- };
1274
- }
1275
-
1276
- /**
1277
- * Check if a flow should pre-extract data before determining the initial step
1278
- */
1279
- private shouldPreExtractData(flow: Flow<TContext, TData>): boolean {
1280
- // Pre-extract if flow has declared required or optional fields
1281
- if (flow.requiredFields && flow.requiredFields.length > 0) {
1282
- return true;
1283
- }
1284
- if (flow.optionalFields && flow.optionalFields.length > 0) {
1285
- return true;
1286
- }
1287
-
1288
- // Pre-extract if any step in the flow collects data
1289
- const steps = flow.getAllSteps();
1290
- const hasDataCollectionSteps = steps.some(
1291
- step => step.collect && step.collect.length > 0
1292
- );
1293
-
1294
- return hasDataCollectionSteps;
1295
- }
1296
-
1297
- /**
1298
- * Build an extraction schema limited to the given subset of agent schema
1299
- * fields. Used by the routing-skip probe so only the current step's collect
1300
- * fields are visible to the extractor.
1301
- */
1302
- private buildFieldRestrictedSchema(
1303
- agentSchema: StructuredSchema,
1304
- fields: string[],
1305
- ): StructuredSchema {
1306
- const properties: Record<string, StructuredSchema> = {};
1307
- for (const field of fields) {
1308
- const prop = agentSchema.properties?.[field];
1309
- if (prop) {
1310
- properties[field] = prop;
1311
- }
1312
- }
1313
- return {
1314
- type: "object",
1315
- description: `Extraction restricted to these fields: ${fields.join(', ')}`,
1316
- properties,
1317
- additionalProperties: false,
1318
- };
1319
- }
1320
-
1321
- /**
1322
- * Pre-extract data from user message when entering a flow
1323
- * This allows skipping steps whose data is already provided
1324
- */
1325
- private async preExtractFlowData(params: {
1326
- route: Flow<TContext, TData>;
1327
- history: Event[];
1328
- context: TContext;
1329
- session: SessionState<TData>;
1330
- signal?: AbortSignal;
1331
- /** When set, extraction is restricted to exactly these schema fields (step-scoped routing-skip probe). */
1332
- restrictToFields?: string[];
1333
- }): Promise<Partial<TData>> {
1334
- const { route: flow, history, signal, restrictToFields } = params;
1335
-
1336
- // Build a schema for data extraction based on flow's fields
1337
- const agentSchema = this.getSchema();
1338
- if (!agentSchema) {
1339
- logger.warn(`[ResponsePipeline] No schema available for pre-extraction`);
1340
- return {};
1341
- }
1342
- const extractionSchema = restrictToFields
1343
- ? this.buildFieldRestrictedSchema(agentSchema, restrictToFields)
1344
- : agentSchema;
1345
-
1346
- // Get last user message
1347
- const lastMessage = getLastMessageFromHistory(history);
1348
-
1349
- // Build extraction prompt
1350
- const extractionPrompt = [
1351
- `Extract any relevant information from the user's message that matches the following data fields.`,
1352
- `Only extract information that is explicitly stated or clearly implied.`,
1353
- ``,
1354
- `User's message: "${lastMessage}"`,
1355
- ``,
1356
- `Extract data for these fields if present:`,
1357
- ];
1358
-
1359
- // Add field descriptions
1360
- if (restrictToFields) {
1361
- extractionPrompt.push(`Only these fields: ${restrictToFields.join(', ')}`);
1362
- } else {
1363
- if (flow.requiredFields) {
1364
- extractionPrompt.push(`Required fields: ${flow.requiredFields.join(', ')}`);
1365
- }
1366
- if (flow.optionalFields) {
1367
- extractionPrompt.push(`Optional fields: ${flow.optionalFields.join(', ')}`);
1368
- }
1369
- }
1370
-
1371
- extractionPrompt.push(
1372
- ``,
1373
- `Return ONLY the extracted data as JSON. If no data can be extracted, return an empty object {}.`
1374
- );
1375
-
1376
- // Convert Event[] to HistoryItem[] for provider call
1377
- const historyItems = eventsToHistory(history);
1378
-
1379
- // Call AI to extract data
1380
- try {
1381
- const result = await this.options.provider.generateMessage<TContext, Partial<TData>>({
1382
- prompt: extractionPrompt.join('\n'),
1383
- history: historyItems,
1384
- context: {} as TContext, // Passed as empty object so AI doesn't "extract" from context
1385
- // NOTE: context is intentionally NOT passed here.
1386
- // Passing context caused the AI to "extract" data from the lead's context
1387
- // (e.g., name, sector, city) instead of from what the user actually said.
1388
- signal,
1389
- parameters: {
1390
- jsonSchema: extractionSchema,
1391
- schemaName: 'data_extraction',
1392
- },
1393
- });
1394
-
1395
- // Every caller merges this into the turn session BEFORE updateCollectedData
1396
- // runs, so an undeclared key is dropped here, at the source
1397
- return dropUndeclaredFields(result.structured || {}, agentSchema);
1398
- } catch (error) {
1399
- logger.error(`[ResponsePipeline] Pre-extraction failed:`, error);
1400
- return {};
1401
- }
1402
- }
1403
-
1404
- }