@falai/agent 3.4.5 → 4.0.0-alpha.10

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 (865) hide show
  1. package/README.md +41 -34
  2. package/dist/cjs/core/Agent.d.ts +29 -378
  3. package/dist/cjs/core/Agent.d.ts.map +1 -1
  4. package/dist/cjs/core/Agent.js +113 -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 +573 -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 +149 -0
  20. package/dist/cjs/core/Prompt.js.map +1 -0
  21. package/dist/cjs/core/Runner.d.ts +171 -0
  22. package/dist/cjs/core/Runner.d.ts.map +1 -0
  23. package/dist/cjs/core/Runner.js +1158 -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 +373 -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 +357 -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 +163 -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/phrases.d.ts +25 -0
  158. package/dist/cjs/utils/phrases.d.ts.map +1 -0
  159. package/dist/cjs/utils/phrases.js +38 -0
  160. package/dist/cjs/utils/phrases.js.map +1 -0
  161. package/dist/cjs/utils/schema.d.ts +50 -0
  162. package/dist/cjs/utils/schema.d.ts.map +1 -0
  163. package/dist/cjs/utils/schema.js +138 -0
  164. package/dist/cjs/utils/schema.js.map +1 -0
  165. package/dist/cjs/utils/streamingMessage.d.ts +3 -2
  166. package/dist/cjs/utils/streamingMessage.d.ts.map +1 -1
  167. package/dist/cjs/utils/streamingMessage.js +38 -4
  168. package/dist/cjs/utils/streamingMessage.js.map +1 -1
  169. package/dist/cjs/utils/template.d.ts +22 -150
  170. package/dist/cjs/utils/template.d.ts.map +1 -1
  171. package/dist/cjs/utils/template.js +64 -359
  172. package/dist/cjs/utils/template.js.map +1 -1
  173. package/dist/cjs/utils/usage.d.ts +19 -0
  174. package/dist/cjs/utils/usage.d.ts.map +1 -0
  175. package/dist/cjs/utils/usage.js +35 -0
  176. package/dist/cjs/utils/usage.js.map +1 -0
  177. package/dist/core/Agent.d.ts +29 -378
  178. package/dist/core/Agent.d.ts.map +1 -1
  179. package/dist/core/Agent.js +116 -1181
  180. package/dist/core/Agent.js.map +1 -1
  181. package/dist/core/CompactionEngine.d.ts.map +1 -1
  182. package/dist/core/CompactionEngine.js +5 -3
  183. package/dist/core/CompactionEngine.js.map +1 -1
  184. package/dist/core/FlowSpec.d.ts +136 -0
  185. package/dist/core/FlowSpec.d.ts.map +1 -0
  186. package/dist/core/FlowSpec.js +567 -0
  187. package/dist/core/FlowSpec.js.map +1 -0
  188. package/dist/core/Migrate.d.ts +38 -0
  189. package/dist/core/Migrate.d.ts.map +1 -0
  190. package/dist/core/Migrate.js +264 -0
  191. package/dist/core/Migrate.js.map +1 -0
  192. package/dist/core/Prompt.d.ts +54 -0
  193. package/dist/core/Prompt.d.ts.map +1 -0
  194. package/dist/core/Prompt.js +139 -0
  195. package/dist/core/Prompt.js.map +1 -0
  196. package/dist/core/Runner.d.ts +171 -0
  197. package/dist/core/Runner.d.ts.map +1 -0
  198. package/dist/core/Runner.js +1154 -0
  199. package/dist/core/Runner.js.map +1 -0
  200. package/dist/core/Speak.d.ts +37 -0
  201. package/dist/core/Speak.d.ts.map +1 -0
  202. package/dist/core/Speak.js +369 -0
  203. package/dist/core/Speak.js.map +1 -0
  204. package/dist/core/Understand.d.ts +28 -0
  205. package/dist/core/Understand.d.ts.map +1 -0
  206. package/dist/core/Understand.js +353 -0
  207. package/dist/core/Understand.js.map +1 -0
  208. package/dist/core/contracts.d.ts +122 -0
  209. package/dist/core/contracts.d.ts.map +1 -0
  210. package/dist/core/contracts.js +10 -0
  211. package/dist/core/contracts.js.map +1 -0
  212. package/dist/core/falai.d.ts +57 -0
  213. package/dist/core/falai.d.ts.map +1 -0
  214. package/dist/core/falai.js +40 -0
  215. package/dist/core/falai.js.map +1 -0
  216. package/dist/core/predicate.d.ts +9 -0
  217. package/dist/core/predicate.d.ts.map +1 -0
  218. package/dist/core/predicate.js +54 -0
  219. package/dist/core/predicate.js.map +1 -0
  220. package/dist/index.d.ts +26 -31
  221. package/dist/index.d.ts.map +1 -1
  222. package/dist/index.js +19 -24
  223. package/dist/index.js.map +1 -1
  224. package/dist/persistence/MemoryStore.d.ts +15 -0
  225. package/dist/persistence/MemoryStore.d.ts.map +1 -0
  226. package/dist/persistence/MemoryStore.js +35 -0
  227. package/dist/persistence/MemoryStore.js.map +1 -0
  228. package/dist/persistence/MongoStore.d.ts +42 -0
  229. package/dist/persistence/MongoStore.d.ts.map +1 -0
  230. package/dist/persistence/MongoStore.js +56 -0
  231. package/dist/persistence/MongoStore.js.map +1 -0
  232. package/dist/persistence/OpenSearchStore.d.ts +86 -0
  233. package/dist/persistence/OpenSearchStore.d.ts.map +1 -0
  234. package/dist/persistence/OpenSearchStore.js +116 -0
  235. package/dist/persistence/OpenSearchStore.js.map +1 -0
  236. package/dist/persistence/PostgresStore.d.ts +41 -0
  237. package/dist/persistence/PostgresStore.d.ts.map +1 -0
  238. package/dist/persistence/PostgresStore.js +54 -0
  239. package/dist/persistence/PostgresStore.js.map +1 -0
  240. package/dist/persistence/PrismaStore.d.ts +65 -0
  241. package/dist/persistence/PrismaStore.d.ts.map +1 -0
  242. package/dist/persistence/PrismaStore.js +91 -0
  243. package/dist/persistence/PrismaStore.js.map +1 -0
  244. package/dist/persistence/RedisStore.d.ts +34 -0
  245. package/dist/persistence/RedisStore.d.ts.map +1 -0
  246. package/dist/persistence/RedisStore.js +57 -0
  247. package/dist/persistence/RedisStore.js.map +1 -0
  248. package/dist/persistence/SQLiteStore.d.ts +45 -0
  249. package/dist/persistence/SQLiteStore.d.ts.map +1 -0
  250. package/dist/persistence/SQLiteStore.js +70 -0
  251. package/dist/persistence/SQLiteStore.js.map +1 -0
  252. package/dist/persistence/sessionRow.d.ts +14 -0
  253. package/dist/persistence/sessionRow.d.ts.map +1 -0
  254. package/dist/persistence/sessionRow.js +45 -0
  255. package/dist/persistence/sessionRow.js.map +1 -0
  256. package/dist/providers/DeepSeekProvider.d.ts.map +1 -1
  257. package/dist/providers/DeepSeekProvider.js +8 -3
  258. package/dist/providers/DeepSeekProvider.js.map +1 -1
  259. package/dist/providers/GeminiProvider.d.ts +4 -3
  260. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  261. package/dist/providers/GeminiProvider.js +4 -3
  262. package/dist/providers/GeminiProvider.js.map +1 -1
  263. package/dist/providers/OpenAICompatibleProvider.d.ts +4 -0
  264. package/dist/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  265. package/dist/providers/OpenAICompatibleProvider.js +2 -0
  266. package/dist/providers/OpenAICompatibleProvider.js.map +1 -1
  267. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  268. package/dist/providers/OpenRouterProvider.js +2 -4
  269. package/dist/providers/OpenRouterProvider.js.map +1 -1
  270. package/dist/providers/ProviderAdapter.d.ts +11 -6
  271. package/dist/providers/ProviderAdapter.d.ts.map +1 -1
  272. package/dist/providers/ProviderAdapter.js +34 -11
  273. package/dist/providers/ProviderAdapter.js.map +1 -1
  274. package/dist/providers/ZaiProvider.d.ts +6 -4
  275. package/dist/providers/ZaiProvider.d.ts.map +1 -1
  276. package/dist/providers/ZaiProvider.js +6 -4
  277. package/dist/providers/ZaiProvider.js.map +1 -1
  278. package/dist/types/agent.d.ts +163 -383
  279. package/dist/types/agent.d.ts.map +1 -1
  280. package/dist/types/agent.js +1 -1
  281. package/dist/types/ai.d.ts +32 -1
  282. package/dist/types/ai.d.ts.map +1 -1
  283. package/dist/types/compaction.d.ts +3 -1
  284. package/dist/types/compaction.d.ts.map +1 -1
  285. package/dist/types/errors.d.ts +9 -12
  286. package/dist/types/errors.d.ts.map +1 -1
  287. package/dist/types/errors.js +12 -15
  288. package/dist/types/errors.js.map +1 -1
  289. package/dist/types/flow.d.ts +265 -513
  290. package/dist/types/flow.d.ts.map +1 -1
  291. package/dist/types/flow.js +7 -1
  292. package/dist/types/flow.js.map +1 -1
  293. package/dist/types/history.d.ts +7 -18
  294. package/dist/types/history.d.ts.map +1 -1
  295. package/dist/types/history.js.map +1 -1
  296. package/dist/types/index.d.ts +9 -15
  297. package/dist/types/index.d.ts.map +1 -1
  298. package/dist/types/index.js +2 -7
  299. package/dist/types/index.js.map +1 -1
  300. package/dist/types/session.d.ts +94 -64
  301. package/dist/types/session.d.ts.map +1 -1
  302. package/dist/types/session.js +5 -1
  303. package/dist/types/session.js.map +1 -1
  304. package/dist/types/tool.d.ts +37 -207
  305. package/dist/types/tool.d.ts.map +1 -1
  306. package/dist/types/tool.js +6 -13
  307. package/dist/types/tool.js.map +1 -1
  308. package/dist/utils/clock.d.ts +28 -0
  309. package/dist/utils/clock.d.ts.map +1 -0
  310. package/dist/utils/clock.js +59 -0
  311. package/dist/utils/clock.js.map +1 -0
  312. package/dist/utils/duration.d.ts +11 -0
  313. package/dist/utils/duration.d.ts.map +1 -0
  314. package/dist/utils/duration.js +26 -0
  315. package/dist/utils/duration.js.map +1 -0
  316. package/dist/utils/history.d.ts +4 -1
  317. package/dist/utils/history.d.ts.map +1 -1
  318. package/dist/utils/history.js +2 -2
  319. package/dist/utils/history.js.map +1 -1
  320. package/dist/utils/index.d.ts +4 -10
  321. package/dist/utils/index.d.ts.map +1 -1
  322. package/dist/utils/index.js +4 -21
  323. package/dist/utils/index.js.map +1 -1
  324. package/dist/utils/json.d.ts +2 -0
  325. package/dist/utils/json.d.ts.map +1 -1
  326. package/dist/utils/json.js +4 -0
  327. package/dist/utils/json.js.map +1 -1
  328. package/dist/utils/outcomes.d.ts +48 -0
  329. package/dist/utils/outcomes.d.ts.map +1 -0
  330. package/dist/utils/outcomes.js +48 -0
  331. package/dist/utils/outcomes.js.map +1 -0
  332. package/dist/utils/phrases.d.ts +25 -0
  333. package/dist/utils/phrases.d.ts.map +1 -0
  334. package/dist/utils/phrases.js +35 -0
  335. package/dist/utils/phrases.js.map +1 -0
  336. package/dist/utils/schema.d.ts +50 -0
  337. package/dist/utils/schema.d.ts.map +1 -0
  338. package/dist/utils/schema.js +129 -0
  339. package/dist/utils/schema.js.map +1 -0
  340. package/dist/utils/streamingMessage.d.ts +3 -2
  341. package/dist/utils/streamingMessage.d.ts.map +1 -1
  342. package/dist/utils/streamingMessage.js +38 -4
  343. package/dist/utils/streamingMessage.js.map +1 -1
  344. package/dist/utils/template.d.ts +22 -150
  345. package/dist/utils/template.d.ts.map +1 -1
  346. package/dist/utils/template.js +61 -351
  347. package/dist/utils/template.js.map +1 -1
  348. package/dist/utils/usage.d.ts +19 -0
  349. package/dist/utils/usage.d.ts.map +1 -0
  350. package/dist/utils/usage.js +31 -0
  351. package/dist/utils/usage.js.map +1 -0
  352. package/docs/README.md +37 -19
  353. package/docs/concepts/architecture.md +117 -239
  354. package/docs/concepts/collection.md +170 -0
  355. package/docs/concepts/pipeline.md +132 -378
  356. package/docs/concepts/runs-and-waits.md +192 -0
  357. package/docs/guides/actions-and-events.md +276 -0
  358. package/docs/guides/branching.md +119 -208
  359. package/docs/guides/compaction.md +63 -158
  360. package/docs/guides/conditions.md +164 -128
  361. package/docs/guides/error-handling.md +170 -164
  362. package/docs/guides/flow-control.md +210 -349
  363. package/docs/guides/flows-from-json.md +224 -0
  364. package/docs/guides/instructions.md +125 -161
  365. package/docs/guides/persistence.md +182 -206
  366. package/docs/guides/streaming.md +50 -114
  367. package/docs/guides/testing.md +284 -0
  368. package/docs/guides/triggers.md +401 -0
  369. package/docs/migration/README.md +8 -15
  370. package/docs/migration/v1-to-v2.md +1 -1
  371. package/docs/migration/v2-3-to-v2-4.md +2 -2
  372. package/docs/migration/v2-6-to-v2-7.md +4 -4
  373. package/docs/migration/v3-to-v4.md +457 -0
  374. package/docs/reference/actions-events-conditions.md +396 -0
  375. package/docs/reference/agent.md +248 -0
  376. package/docs/reference/branches.md +75 -203
  377. package/docs/reference/errors.md +188 -144
  378. package/docs/reference/fields.md +125 -0
  379. package/docs/reference/flow-spec.md +248 -0
  380. package/docs/reference/flow.md +104 -192
  381. package/docs/reference/instruction.md +83 -137
  382. package/docs/reference/outcomes.md +273 -0
  383. package/docs/reference/providers.md +525 -302
  384. package/docs/reference/session.md +210 -0
  385. package/docs/reference/step.md +194 -312
  386. package/docs/reference/stores.md +496 -0
  387. package/docs/reference/tool.md +162 -231
  388. package/docs/reference/trigger.md +200 -0
  389. package/docs/rfc/v4-one-flow.md +477 -0
  390. package/docs/start/01-install.md +59 -44
  391. package/docs/start/02-first-agent.md +97 -147
  392. package/docs/start/03-collect-data.md +78 -183
  393. package/docs/start/04-add-tools.md +159 -227
  394. package/docs/start/05-go-to-production.md +181 -163
  395. package/examples/01-quickstart.ts +26 -16
  396. package/examples/02-fields.ts +75 -0
  397. package/examples/03-tools.ts +79 -119
  398. package/examples/04-instructions.ts +60 -87
  399. package/examples/05-branches.ts +78 -0
  400. package/examples/06-triggers-and-waits.ts +149 -0
  401. package/examples/07-streaming.ts +34 -60
  402. package/examples/08-store-and-migration.ts +97 -0
  403. package/examples/09-flows-from-json.ts +107 -0
  404. package/package.json +11 -6
  405. package/src/core/Agent.ts +126 -1512
  406. package/src/core/CompactionEngine.ts +7 -4
  407. package/src/core/FlowSpec.ts +778 -0
  408. package/src/core/Migrate.ts +256 -0
  409. package/src/core/Prompt.ts +162 -0
  410. package/src/core/Runner.ts +1214 -0
  411. package/src/core/Speak.ts +460 -0
  412. package/src/core/Understand.ts +423 -0
  413. package/src/core/contracts.ts +111 -0
  414. package/src/core/falai.ts +86 -0
  415. package/src/core/predicate.ts +56 -0
  416. package/src/index.ts +120 -147
  417. package/src/persistence/MemoryStore.ts +37 -0
  418. package/src/persistence/MongoStore.ts +89 -0
  419. package/src/persistence/OpenSearchStore.ts +153 -0
  420. package/src/persistence/PostgresStore.ts +89 -0
  421. package/src/persistence/PrismaStore.ts +127 -0
  422. package/src/persistence/RedisStore.ts +90 -0
  423. package/src/persistence/SQLiteStore.ts +103 -0
  424. package/src/persistence/sessionRow.ts +45 -0
  425. package/src/providers/DeepSeekProvider.ts +8 -3
  426. package/src/providers/GeminiProvider.ts +4 -3
  427. package/src/providers/OpenAICompatibleProvider.ts +6 -0
  428. package/src/providers/OpenRouterProvider.ts +2 -4
  429. package/src/providers/ProviderAdapter.ts +46 -13
  430. package/src/providers/ZaiProvider.ts +6 -4
  431. package/src/types/agent.ts +135 -397
  432. package/src/types/ai.ts +33 -1
  433. package/src/types/compaction.ts +3 -1
  434. package/src/types/errors.ts +13 -16
  435. package/src/types/flow.ts +249 -550
  436. package/src/types/history.ts +7 -20
  437. package/src/types/index.ts +88 -139
  438. package/src/types/session.ts +135 -70
  439. package/src/types/tool.ts +42 -267
  440. package/src/utils/clock.ts +70 -0
  441. package/src/utils/duration.ts +33 -0
  442. package/src/utils/history.ts +3 -2
  443. package/src/utils/index.ts +8 -66
  444. package/src/utils/json.ts +5 -0
  445. package/src/utils/outcomes.ts +56 -0
  446. package/src/utils/phrases.ts +40 -0
  447. package/src/utils/schema.ts +145 -0
  448. package/src/utils/streamingMessage.ts +34 -4
  449. package/src/utils/template.ts +63 -418
  450. package/src/utils/usage.ts +37 -0
  451. package/dist/adapters/MemoryAdapter.d.ts +0 -47
  452. package/dist/adapters/MemoryAdapter.d.ts.map +0 -1
  453. package/dist/adapters/MemoryAdapter.js +0 -204
  454. package/dist/adapters/MemoryAdapter.js.map +0 -1
  455. package/dist/adapters/MongoAdapter.d.ts +0 -97
  456. package/dist/adapters/MongoAdapter.d.ts.map +0 -1
  457. package/dist/adapters/MongoAdapter.js +0 -196
  458. package/dist/adapters/MongoAdapter.js.map +0 -1
  459. package/dist/adapters/OpenSearchAdapter.d.ts +0 -169
  460. package/dist/adapters/OpenSearchAdapter.d.ts.map +0 -1
  461. package/dist/adapters/OpenSearchAdapter.js +0 -471
  462. package/dist/adapters/OpenSearchAdapter.js.map +0 -1
  463. package/dist/adapters/PostgreSQLAdapter.d.ts +0 -85
  464. package/dist/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  465. package/dist/adapters/PostgreSQLAdapter.js +0 -308
  466. package/dist/adapters/PostgreSQLAdapter.js.map +0 -1
  467. package/dist/adapters/PrismaAdapter.d.ts +0 -115
  468. package/dist/adapters/PrismaAdapter.d.ts.map +0 -1
  469. package/dist/adapters/PrismaAdapter.js +0 -406
  470. package/dist/adapters/PrismaAdapter.js.map +0 -1
  471. package/dist/adapters/RedisAdapter.d.ts +0 -72
  472. package/dist/adapters/RedisAdapter.d.ts.map +0 -1
  473. package/dist/adapters/RedisAdapter.js +0 -286
  474. package/dist/adapters/RedisAdapter.js.map +0 -1
  475. package/dist/adapters/SQLiteAdapter.d.ts +0 -86
  476. package/dist/adapters/SQLiteAdapter.d.ts.map +0 -1
  477. package/dist/adapters/SQLiteAdapter.js +0 -337
  478. package/dist/adapters/SQLiteAdapter.js.map +0 -1
  479. package/dist/adapters/index.d.ts +0 -17
  480. package/dist/adapters/index.d.ts.map +0 -1
  481. package/dist/adapters/index.js +0 -11
  482. package/dist/adapters/index.js.map +0 -1
  483. package/dist/adapters/sessionRow.d.ts +0 -22
  484. package/dist/adapters/sessionRow.d.ts.map +0 -1
  485. package/dist/adapters/sessionRow.js +0 -48
  486. package/dist/adapters/sessionRow.js.map +0 -1
  487. package/dist/cjs/adapters/MemoryAdapter.d.ts +0 -47
  488. package/dist/cjs/adapters/MemoryAdapter.d.ts.map +0 -1
  489. package/dist/cjs/adapters/MemoryAdapter.js +0 -208
  490. package/dist/cjs/adapters/MemoryAdapter.js.map +0 -1
  491. package/dist/cjs/adapters/MongoAdapter.d.ts +0 -97
  492. package/dist/cjs/adapters/MongoAdapter.d.ts.map +0 -1
  493. package/dist/cjs/adapters/MongoAdapter.js +0 -200
  494. package/dist/cjs/adapters/MongoAdapter.js.map +0 -1
  495. package/dist/cjs/adapters/OpenSearchAdapter.d.ts +0 -169
  496. package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +0 -1
  497. package/dist/cjs/adapters/OpenSearchAdapter.js +0 -475
  498. package/dist/cjs/adapters/OpenSearchAdapter.js.map +0 -1
  499. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +0 -85
  500. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  501. package/dist/cjs/adapters/PostgreSQLAdapter.js +0 -312
  502. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +0 -1
  503. package/dist/cjs/adapters/PrismaAdapter.d.ts +0 -115
  504. package/dist/cjs/adapters/PrismaAdapter.d.ts.map +0 -1
  505. package/dist/cjs/adapters/PrismaAdapter.js +0 -410
  506. package/dist/cjs/adapters/PrismaAdapter.js.map +0 -1
  507. package/dist/cjs/adapters/RedisAdapter.d.ts +0 -72
  508. package/dist/cjs/adapters/RedisAdapter.d.ts.map +0 -1
  509. package/dist/cjs/adapters/RedisAdapter.js +0 -290
  510. package/dist/cjs/adapters/RedisAdapter.js.map +0 -1
  511. package/dist/cjs/adapters/SQLiteAdapter.d.ts +0 -86
  512. package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +0 -1
  513. package/dist/cjs/adapters/SQLiteAdapter.js +0 -341
  514. package/dist/cjs/adapters/SQLiteAdapter.js.map +0 -1
  515. package/dist/cjs/adapters/index.d.ts +0 -17
  516. package/dist/cjs/adapters/index.d.ts.map +0 -1
  517. package/dist/cjs/adapters/index.js +0 -21
  518. package/dist/cjs/adapters/index.js.map +0 -1
  519. package/dist/cjs/adapters/sessionRow.d.ts +0 -22
  520. package/dist/cjs/adapters/sessionRow.d.ts.map +0 -1
  521. package/dist/cjs/adapters/sessionRow.js +0 -52
  522. package/dist/cjs/adapters/sessionRow.js.map +0 -1
  523. package/dist/cjs/constants/index.d.ts +0 -1
  524. package/dist/cjs/constants/index.d.ts.map +0 -1
  525. package/dist/cjs/constants/index.js +0 -4
  526. package/dist/cjs/constants/index.js.map +0 -1
  527. package/dist/cjs/core/AutoChainExecutor.d.ts +0 -97
  528. package/dist/cjs/core/AutoChainExecutor.d.ts.map +0 -1
  529. package/dist/cjs/core/AutoChainExecutor.js +0 -288
  530. package/dist/cjs/core/AutoChainExecutor.js.map +0 -1
  531. package/dist/cjs/core/BranchEvaluator.d.ts +0 -55
  532. package/dist/cjs/core/BranchEvaluator.d.ts.map +0 -1
  533. package/dist/cjs/core/BranchEvaluator.js +0 -125
  534. package/dist/cjs/core/BranchEvaluator.js.map +0 -1
  535. package/dist/cjs/core/DirectiveChainTracker.d.ts +0 -49
  536. package/dist/cjs/core/DirectiveChainTracker.d.ts.map +0 -1
  537. package/dist/cjs/core/DirectiveChainTracker.js +0 -121
  538. package/dist/cjs/core/DirectiveChainTracker.js.map +0 -1
  539. package/dist/cjs/core/Events.d.ts +0 -26
  540. package/dist/cjs/core/Events.d.ts.map +0 -1
  541. package/dist/cjs/core/Events.js +0 -144
  542. package/dist/cjs/core/Events.js.map +0 -1
  543. package/dist/cjs/core/Flow.d.ts +0 -183
  544. package/dist/cjs/core/Flow.d.ts.map +0 -1
  545. package/dist/cjs/core/Flow.js +0 -551
  546. package/dist/cjs/core/Flow.js.map +0 -1
  547. package/dist/cjs/core/FlowRouter.d.ts +0 -183
  548. package/dist/cjs/core/FlowRouter.d.ts.map +0 -1
  549. package/dist/cjs/core/FlowRouter.js +0 -1047
  550. package/dist/cjs/core/FlowRouter.js.map +0 -1
  551. package/dist/cjs/core/PersistenceManager.d.ts +0 -114
  552. package/dist/cjs/core/PersistenceManager.d.ts.map +0 -1
  553. package/dist/cjs/core/PersistenceManager.js +0 -336
  554. package/dist/cjs/core/PersistenceManager.js.map +0 -1
  555. package/dist/cjs/core/PromptComposer.d.ts +0 -47
  556. package/dist/cjs/core/PromptComposer.d.ts.map +0 -1
  557. package/dist/cjs/core/PromptComposer.js +0 -397
  558. package/dist/cjs/core/PromptComposer.js.map +0 -1
  559. package/dist/cjs/core/PromptSectionCache.d.ts +0 -48
  560. package/dist/cjs/core/PromptSectionCache.d.ts.map +0 -1
  561. package/dist/cjs/core/PromptSectionCache.js +0 -108
  562. package/dist/cjs/core/PromptSectionCache.js.map +0 -1
  563. package/dist/cjs/core/ResponseEngine.d.ts +0 -43
  564. package/dist/cjs/core/ResponseEngine.d.ts.map +0 -1
  565. package/dist/cjs/core/ResponseEngine.js +0 -235
  566. package/dist/cjs/core/ResponseEngine.js.map +0 -1
  567. package/dist/cjs/core/ResponseGenerationError.d.ts +0 -30
  568. package/dist/cjs/core/ResponseGenerationError.d.ts.map +0 -1
  569. package/dist/cjs/core/ResponseGenerationError.js +0 -35
  570. package/dist/cjs/core/ResponseGenerationError.js.map +0 -1
  571. package/dist/cjs/core/ResponseModal.d.ts +0 -305
  572. package/dist/cjs/core/ResponseModal.d.ts.map +0 -1
  573. package/dist/cjs/core/ResponseModal.js +0 -1414
  574. package/dist/cjs/core/ResponseModal.js.map +0 -1
  575. package/dist/cjs/core/ResponsePipeline.d.ts +0 -220
  576. package/dist/cjs/core/ResponsePipeline.d.ts.map +0 -1
  577. package/dist/cjs/core/ResponsePipeline.js +0 -1040
  578. package/dist/cjs/core/ResponsePipeline.js.map +0 -1
  579. package/dist/cjs/core/SessionFinalizer.d.ts +0 -34
  580. package/dist/cjs/core/SessionFinalizer.d.ts.map +0 -1
  581. package/dist/cjs/core/SessionFinalizer.js +0 -88
  582. package/dist/cjs/core/SessionFinalizer.js.map +0 -1
  583. package/dist/cjs/core/SessionManager.d.ts +0 -112
  584. package/dist/cjs/core/SessionManager.d.ts.map +0 -1
  585. package/dist/cjs/core/SessionManager.js +0 -308
  586. package/dist/cjs/core/SessionManager.js.map +0 -1
  587. package/dist/cjs/core/SignalCoordinator.d.ts +0 -103
  588. package/dist/cjs/core/SignalCoordinator.d.ts.map +0 -1
  589. package/dist/cjs/core/SignalCoordinator.js +0 -207
  590. package/dist/cjs/core/SignalCoordinator.js.map +0 -1
  591. package/dist/cjs/core/SignalEvaluator.d.ts +0 -86
  592. package/dist/cjs/core/SignalEvaluator.d.ts.map +0 -1
  593. package/dist/cjs/core/SignalEvaluator.js +0 -319
  594. package/dist/cjs/core/SignalEvaluator.js.map +0 -1
  595. package/dist/cjs/core/SignalProcessor.d.ts +0 -152
  596. package/dist/cjs/core/SignalProcessor.d.ts.map +0 -1
  597. package/dist/cjs/core/SignalProcessor.js +0 -505
  598. package/dist/cjs/core/SignalProcessor.js.map +0 -1
  599. package/dist/cjs/core/Step.d.ts +0 -184
  600. package/dist/cjs/core/Step.d.ts.map +0 -1
  601. package/dist/cjs/core/Step.js +0 -599
  602. package/dist/cjs/core/Step.js.map +0 -1
  603. package/dist/cjs/core/StepLifecycle.d.ts +0 -43
  604. package/dist/cjs/core/StepLifecycle.d.ts.map +0 -1
  605. package/dist/cjs/core/StepLifecycle.js +0 -180
  606. package/dist/cjs/core/StepLifecycle.js.map +0 -1
  607. package/dist/cjs/core/StreamingToolExecutor.d.ts +0 -142
  608. package/dist/cjs/core/StreamingToolExecutor.d.ts.map +0 -1
  609. package/dist/cjs/core/StreamingToolExecutor.js +0 -490
  610. package/dist/cjs/core/StreamingToolExecutor.js.map +0 -1
  611. package/dist/cjs/core/ToolLoopExecutor.d.ts +0 -133
  612. package/dist/cjs/core/ToolLoopExecutor.d.ts.map +0 -1
  613. package/dist/cjs/core/ToolLoopExecutor.js +0 -568
  614. package/dist/cjs/core/ToolLoopExecutor.js.map +0 -1
  615. package/dist/cjs/core/ToolManager.d.ts +0 -250
  616. package/dist/cjs/core/ToolManager.d.ts.map +0 -1
  617. package/dist/cjs/core/ToolManager.js +0 -1104
  618. package/dist/cjs/core/ToolManager.js.map +0 -1
  619. package/dist/cjs/core/createAgent.d.ts +0 -35
  620. package/dist/cjs/core/createAgent.d.ts.map +0 -1
  621. package/dist/cjs/core/createAgent.js +0 -39
  622. package/dist/cjs/core/createAgent.js.map +0 -1
  623. package/dist/cjs/core/flow-namespace.d.ts +0 -64
  624. package/dist/cjs/core/flow-namespace.d.ts.map +0 -1
  625. package/dist/cjs/core/flow-namespace.js +0 -182
  626. package/dist/cjs/core/flow-namespace.js.map +0 -1
  627. package/dist/cjs/core/toolGates.d.ts +0 -24
  628. package/dist/cjs/core/toolGates.d.ts.map +0 -1
  629. package/dist/cjs/core/toolGates.js +0 -52
  630. package/dist/cjs/core/toolGates.js.map +0 -1
  631. package/dist/cjs/types/persistence.d.ts +0 -254
  632. package/dist/cjs/types/persistence.d.ts.map +0 -1
  633. package/dist/cjs/types/persistence.js +0 -7
  634. package/dist/cjs/types/persistence.js.map +0 -1
  635. package/dist/cjs/types/prompt-cache.d.ts +0 -15
  636. package/dist/cjs/types/prompt-cache.d.ts.map +0 -1
  637. package/dist/cjs/types/prompt-cache.js +0 -6
  638. package/dist/cjs/types/prompt-cache.js.map +0 -1
  639. package/dist/cjs/types/signals.d.ts +0 -263
  640. package/dist/cjs/types/signals.d.ts.map +0 -1
  641. package/dist/cjs/types/signals.js +0 -11
  642. package/dist/cjs/types/signals.js.map +0 -1
  643. package/dist/cjs/types/template.d.ts +0 -84
  644. package/dist/cjs/types/template.d.ts.map +0 -1
  645. package/dist/cjs/types/template.js +0 -3
  646. package/dist/cjs/types/template.js.map +0 -1
  647. package/dist/cjs/utils/condition.d.ts +0 -63
  648. package/dist/cjs/utils/condition.d.ts.map +0 -1
  649. package/dist/cjs/utils/condition.js +0 -239
  650. package/dist/cjs/utils/condition.js.map +0 -1
  651. package/dist/cjs/utils/event.d.ts +0 -6
  652. package/dist/cjs/utils/event.d.ts.map +0 -1
  653. package/dist/cjs/utils/event.js +0 -20
  654. package/dist/cjs/utils/event.js.map +0 -1
  655. package/dist/cjs/utils/id.d.ts +0 -33
  656. package/dist/cjs/utils/id.d.ts.map +0 -1
  657. package/dist/cjs/utils/id.js +0 -84
  658. package/dist/cjs/utils/id.js.map +0 -1
  659. package/dist/cjs/utils/serialize.d.ts +0 -36
  660. package/dist/cjs/utils/serialize.d.ts.map +0 -1
  661. package/dist/cjs/utils/serialize.js +0 -77
  662. package/dist/cjs/utils/serialize.js.map +0 -1
  663. package/dist/cjs/utils/session.d.ts +0 -124
  664. package/dist/cjs/utils/session.d.ts.map +0 -1
  665. package/dist/cjs/utils/session.js +0 -396
  666. package/dist/cjs/utils/session.js.map +0 -1
  667. package/dist/constants/index.d.ts +0 -2
  668. package/dist/constants/index.d.ts.map +0 -1
  669. package/dist/constants/index.js +0 -4
  670. package/dist/constants/index.js.map +0 -1
  671. package/dist/core/AutoChainExecutor.d.ts +0 -97
  672. package/dist/core/AutoChainExecutor.d.ts.map +0 -1
  673. package/dist/core/AutoChainExecutor.js +0 -284
  674. package/dist/core/AutoChainExecutor.js.map +0 -1
  675. package/dist/core/BranchEvaluator.d.ts +0 -55
  676. package/dist/core/BranchEvaluator.d.ts.map +0 -1
  677. package/dist/core/BranchEvaluator.js +0 -121
  678. package/dist/core/BranchEvaluator.js.map +0 -1
  679. package/dist/core/DirectiveChainTracker.d.ts +0 -49
  680. package/dist/core/DirectiveChainTracker.d.ts.map +0 -1
  681. package/dist/core/DirectiveChainTracker.js +0 -117
  682. package/dist/core/DirectiveChainTracker.js.map +0 -1
  683. package/dist/core/Events.d.ts +0 -26
  684. package/dist/core/Events.d.ts.map +0 -1
  685. package/dist/core/Events.js +0 -137
  686. package/dist/core/Events.js.map +0 -1
  687. package/dist/core/Flow.d.ts +0 -183
  688. package/dist/core/Flow.d.ts.map +0 -1
  689. package/dist/core/Flow.js +0 -547
  690. package/dist/core/Flow.js.map +0 -1
  691. package/dist/core/FlowRouter.d.ts +0 -183
  692. package/dist/core/FlowRouter.d.ts.map +0 -1
  693. package/dist/core/FlowRouter.js +0 -1043
  694. package/dist/core/FlowRouter.js.map +0 -1
  695. package/dist/core/PersistenceManager.d.ts +0 -114
  696. package/dist/core/PersistenceManager.d.ts.map +0 -1
  697. package/dist/core/PersistenceManager.js +0 -332
  698. package/dist/core/PersistenceManager.js.map +0 -1
  699. package/dist/core/PromptComposer.d.ts +0 -47
  700. package/dist/core/PromptComposer.d.ts.map +0 -1
  701. package/dist/core/PromptComposer.js +0 -393
  702. package/dist/core/PromptComposer.js.map +0 -1
  703. package/dist/core/PromptSectionCache.d.ts +0 -48
  704. package/dist/core/PromptSectionCache.d.ts.map +0 -1
  705. package/dist/core/PromptSectionCache.js +0 -104
  706. package/dist/core/PromptSectionCache.js.map +0 -1
  707. package/dist/core/ResponseEngine.d.ts +0 -43
  708. package/dist/core/ResponseEngine.d.ts.map +0 -1
  709. package/dist/core/ResponseEngine.js +0 -231
  710. package/dist/core/ResponseEngine.js.map +0 -1
  711. package/dist/core/ResponseGenerationError.d.ts +0 -30
  712. package/dist/core/ResponseGenerationError.d.ts.map +0 -1
  713. package/dist/core/ResponseGenerationError.js +0 -31
  714. package/dist/core/ResponseGenerationError.js.map +0 -1
  715. package/dist/core/ResponseModal.d.ts +0 -305
  716. package/dist/core/ResponseModal.d.ts.map +0 -1
  717. package/dist/core/ResponseModal.js +0 -1410
  718. package/dist/core/ResponseModal.js.map +0 -1
  719. package/dist/core/ResponsePipeline.d.ts +0 -220
  720. package/dist/core/ResponsePipeline.d.ts.map +0 -1
  721. package/dist/core/ResponsePipeline.js +0 -1035
  722. package/dist/core/ResponsePipeline.js.map +0 -1
  723. package/dist/core/SessionFinalizer.d.ts +0 -34
  724. package/dist/core/SessionFinalizer.d.ts.map +0 -1
  725. package/dist/core/SessionFinalizer.js +0 -84
  726. package/dist/core/SessionFinalizer.js.map +0 -1
  727. package/dist/core/SessionManager.d.ts +0 -112
  728. package/dist/core/SessionManager.d.ts.map +0 -1
  729. package/dist/core/SessionManager.js +0 -301
  730. package/dist/core/SessionManager.js.map +0 -1
  731. package/dist/core/SignalCoordinator.d.ts +0 -103
  732. package/dist/core/SignalCoordinator.d.ts.map +0 -1
  733. package/dist/core/SignalCoordinator.js +0 -203
  734. package/dist/core/SignalCoordinator.js.map +0 -1
  735. package/dist/core/SignalEvaluator.d.ts +0 -86
  736. package/dist/core/SignalEvaluator.d.ts.map +0 -1
  737. package/dist/core/SignalEvaluator.js +0 -312
  738. package/dist/core/SignalEvaluator.js.map +0 -1
  739. package/dist/core/SignalProcessor.d.ts +0 -152
  740. package/dist/core/SignalProcessor.d.ts.map +0 -1
  741. package/dist/core/SignalProcessor.js +0 -498
  742. package/dist/core/SignalProcessor.js.map +0 -1
  743. package/dist/core/Step.d.ts +0 -184
  744. package/dist/core/Step.d.ts.map +0 -1
  745. package/dist/core/Step.js +0 -594
  746. package/dist/core/Step.js.map +0 -1
  747. package/dist/core/StepLifecycle.d.ts +0 -43
  748. package/dist/core/StepLifecycle.d.ts.map +0 -1
  749. package/dist/core/StepLifecycle.js +0 -176
  750. package/dist/core/StepLifecycle.js.map +0 -1
  751. package/dist/core/StreamingToolExecutor.d.ts +0 -142
  752. package/dist/core/StreamingToolExecutor.d.ts.map +0 -1
  753. package/dist/core/StreamingToolExecutor.js +0 -483
  754. package/dist/core/StreamingToolExecutor.js.map +0 -1
  755. package/dist/core/ToolLoopExecutor.d.ts +0 -133
  756. package/dist/core/ToolLoopExecutor.d.ts.map +0 -1
  757. package/dist/core/ToolLoopExecutor.js +0 -564
  758. package/dist/core/ToolLoopExecutor.js.map +0 -1
  759. package/dist/core/ToolManager.d.ts +0 -250
  760. package/dist/core/ToolManager.d.ts.map +0 -1
  761. package/dist/core/ToolManager.js +0 -1098
  762. package/dist/core/ToolManager.js.map +0 -1
  763. package/dist/core/createAgent.d.ts +0 -35
  764. package/dist/core/createAgent.d.ts.map +0 -1
  765. package/dist/core/createAgent.js +0 -36
  766. package/dist/core/createAgent.js.map +0 -1
  767. package/dist/core/flow-namespace.d.ts +0 -64
  768. package/dist/core/flow-namespace.d.ts.map +0 -1
  769. package/dist/core/flow-namespace.js +0 -179
  770. package/dist/core/flow-namespace.js.map +0 -1
  771. package/dist/core/toolGates.d.ts +0 -24
  772. package/dist/core/toolGates.d.ts.map +0 -1
  773. package/dist/core/toolGates.js +0 -49
  774. package/dist/core/toolGates.js.map +0 -1
  775. package/dist/types/persistence.d.ts +0 -254
  776. package/dist/types/persistence.d.ts.map +0 -1
  777. package/dist/types/persistence.js +0 -6
  778. package/dist/types/persistence.js.map +0 -1
  779. package/dist/types/prompt-cache.d.ts +0 -15
  780. package/dist/types/prompt-cache.d.ts.map +0 -1
  781. package/dist/types/prompt-cache.js +0 -5
  782. package/dist/types/prompt-cache.js.map +0 -1
  783. package/dist/types/signals.d.ts +0 -263
  784. package/dist/types/signals.d.ts.map +0 -1
  785. package/dist/types/signals.js +0 -10
  786. package/dist/types/signals.js.map +0 -1
  787. package/dist/types/template.d.ts +0 -84
  788. package/dist/types/template.d.ts.map +0 -1
  789. package/dist/types/template.js +0 -2
  790. package/dist/types/template.js.map +0 -1
  791. package/dist/utils/condition.d.ts +0 -63
  792. package/dist/utils/condition.d.ts.map +0 -1
  793. package/dist/utils/condition.js +0 -230
  794. package/dist/utils/condition.js.map +0 -1
  795. package/dist/utils/event.d.ts +0 -6
  796. package/dist/utils/event.d.ts.map +0 -1
  797. package/dist/utils/event.js +0 -17
  798. package/dist/utils/event.js.map +0 -1
  799. package/dist/utils/id.d.ts +0 -33
  800. package/dist/utils/id.d.ts.map +0 -1
  801. package/dist/utils/id.js +0 -77
  802. package/dist/utils/id.js.map +0 -1
  803. package/dist/utils/serialize.d.ts +0 -36
  804. package/dist/utils/serialize.d.ts.map +0 -1
  805. package/dist/utils/serialize.js +0 -72
  806. package/dist/utils/serialize.js.map +0 -1
  807. package/dist/utils/session.d.ts +0 -124
  808. package/dist/utils/session.d.ts.map +0 -1
  809. package/dist/utils/session.js +0 -379
  810. package/dist/utils/session.js.map +0 -1
  811. package/docs/concepts/directives.md +0 -369
  812. package/docs/reference/adapters.md +0 -543
  813. package/docs/reference/create-agent.md +0 -216
  814. package/docs/reference/directive.md +0 -242
  815. package/docs/reference/signals.md +0 -368
  816. package/examples/02-data-extraction.ts +0 -90
  817. package/examples/05-branching.ts +0 -140
  818. package/examples/06-flow-control.ts +0 -103
  819. package/examples/08-persistence.ts +0 -98
  820. package/examples/09-signals.ts +0 -144
  821. package/src/adapters/MemoryAdapter.ts +0 -281
  822. package/src/adapters/MongoAdapter.ts +0 -341
  823. package/src/adapters/OpenSearchAdapter.ts +0 -693
  824. package/src/adapters/PostgreSQLAdapter.ts +0 -487
  825. package/src/adapters/PrismaAdapter.ts +0 -617
  826. package/src/adapters/RedisAdapter.ts +0 -439
  827. package/src/adapters/SQLiteAdapter.ts +0 -496
  828. package/src/adapters/index.ts +0 -43
  829. package/src/adapters/sessionRow.ts +0 -57
  830. package/src/constants/index.ts +0 -2
  831. package/src/core/AutoChainExecutor.ts +0 -397
  832. package/src/core/BranchEvaluator.ts +0 -161
  833. package/src/core/DirectiveChainTracker.ts +0 -144
  834. package/src/core/Events.ts +0 -164
  835. package/src/core/Flow.ts +0 -665
  836. package/src/core/FlowRouter.ts +0 -1540
  837. package/src/core/PersistenceManager.ts +0 -446
  838. package/src/core/PromptComposer.ts +0 -448
  839. package/src/core/PromptSectionCache.ts +0 -125
  840. package/src/core/ResponseEngine.ts +0 -338
  841. package/src/core/ResponseGenerationError.ts +0 -53
  842. package/src/core/ResponseModal.ts +0 -1902
  843. package/src/core/ResponsePipeline.ts +0 -1404
  844. package/src/core/SessionFinalizer.ts +0 -108
  845. package/src/core/SessionManager.ts +0 -372
  846. package/src/core/SignalCoordinator.ts +0 -263
  847. package/src/core/SignalEvaluator.ts +0 -404
  848. package/src/core/SignalProcessor.ts +0 -663
  849. package/src/core/Step.ts +0 -782
  850. package/src/core/StepLifecycle.ts +0 -242
  851. package/src/core/StreamingToolExecutor.ts +0 -609
  852. package/src/core/ToolLoopExecutor.ts +0 -749
  853. package/src/core/ToolManager.ts +0 -1379
  854. package/src/core/createAgent.ts +0 -40
  855. package/src/core/flow-namespace.ts +0 -227
  856. package/src/core/toolGates.ts +0 -72
  857. package/src/types/persistence.ts +0 -303
  858. package/src/types/prompt-cache.ts +0 -17
  859. package/src/types/signals.ts +0 -338
  860. package/src/types/template.ts +0 -98
  861. package/src/utils/condition.ts +0 -296
  862. package/src/utils/event.ts +0 -16
  863. package/src/utils/id.ts +0 -91
  864. package/src/utils/serialize.ts +0 -86
  865. package/src/utils/session.ts +0 -501
@@ -1,1902 +0,0 @@
1
- /**
2
- * ResponseModal handles all response generation logic for the Agent
3
- * Provides both streaming and non-streaming response generation with unified logic
4
- */
5
-
6
- import type {
7
- AgentOptions,
8
- AgentResponse,
9
- AgentResponseStreamChunk,
10
- EndedFlow,
11
- History,
12
- SessionState,
13
- StepRef,
14
- HistoryItem,
15
- Event,
16
- AgentStructuredResponse,
17
- GenerateMessageStreamChunk,
18
- StoppedReason,
19
- ScopedInstructions,
20
- AppliedInstruction,
21
- Directive,
22
- Instruction,
23
- Term,
24
- StructuredSchema,
25
- CompactionOptions,
26
- } from "../types/index.js";
27
- import type { SignalFiring } from "../types/signals.js";
28
- import type { SessionManager } from "./SessionManager.js";
29
- import type { SignalProcessor } from "./SignalProcessor.js";
30
- import type { PromptSectionCache } from "./PromptSectionCache.js";
31
- import type { FlowRouter } from "./FlowRouter.js";
32
- import type { PersistenceManager } from "./PersistenceManager.js";
33
- import type { Flow } from "./Flow.js";
34
- import { Step } from "./Step.js";
35
- import { ResponseEngine } from "./ResponseEngine.js";
36
- import { ResponsePipeline } from "./ResponsePipeline.js";
37
- import { AutoChainExecutor, type AutoChainResult } from "./AutoChainExecutor.js";
38
- import { StepLifecycle } from "./StepLifecycle.js";
39
- import { SessionFinalizer } from "./SessionFinalizer.js";
40
- import { ToolLoopExecutor } from "./ToolLoopExecutor.js";
41
- import { flow } from "./flow-namespace.js";
42
- import { SignalCoordinator } from "./SignalCoordinator.js";
43
- import { ResponseGenerationError } from "./ResponseGenerationError.js";
44
- import { ProviderError, SessionConflictError } from "../types/errors.js";
45
- import { cloneDeep, mergeCollected, logger, historyToEvents, completeCurrentFlow, render, userMessage, assistantMessage } from "../utils/index.js";
46
- import { createTemplateContext } from "../utils/template.js";
47
- import { StreamingMessageDecoder } from "../utils/streamingMessage.js";
48
- import { extractEmbeddedJSONObject, isJSONShaped, tryParseJSONResponse } from "../utils/json.js";
49
- import type { ToolManager } from "./ToolManager.js";
50
-
51
- /**
52
- * The narrow surface ResponseModal (and its collaborators) need from the
53
- * Agent. Agent implements this; the response layer is constructible and
54
- * testable against this interface without a full Agent.
55
- */
56
- export interface ResponseModalDeps<TContext = unknown, TData = unknown> {
57
- /** Session manager (history, live session, sync). */
58
- readonly session: SessionManager<TData>;
59
- /** Tool registry/resolver and single-tool executor. */
60
- readonly tool: ToolManager<TContext, TData>;
61
- readonly signalProcessor: SignalProcessor<TContext, TData> | undefined;
62
- readonly promptSectionCache: PromptSectionCache;
63
- readonly instructions: Instruction<TContext, TData>[];
64
- readonly schema: StructuredSchema | undefined;
65
- readonly maxAutoStepsPerTurn: number;
66
- /** The agent's live session reference (read and replaced at finalize). */
67
- currentSession: SessionState<TData> | undefined;
68
- getAgentOptions(): AgentOptions<TContext, TData>;
69
- getFlows(): Flow<TContext, TData>[];
70
- getTerms(): Term<TContext, TData>[];
71
- getFlowRouter(): FlowRouter<TContext, TData>;
72
- getContext(): Promise<TContext | undefined>;
73
- getCompactionOptions(): CompactionOptions | undefined;
74
- getPersistenceManager(): PersistenceManager<TData> | undefined;
75
- getUpdateDataMethod(): (
76
- session: SessionState<TData>,
77
- dataUpdate: Partial<TData>
78
- ) => Promise<SessionState<TData>>;
79
- updateContext(updates: Partial<TContext>): Promise<void>;
80
- updateCollectedData(updates: Partial<TData>): Promise<void>;
81
- /** Drain data staged before any session existed. */
82
- consumePendingData(): Partial<TData>;
83
- }
84
-
85
- /**
86
- * Configuration options for ResponseModal
87
- */
88
- export interface ResponseModalOptions {
89
- /** Maximum number of tool loops allowed during response generation */
90
- maxToolLoops?: number;
91
- /** Enable automatic session saving after response generation */
92
- enableAutoSave?: boolean;
93
- /** Enable debug mode for detailed logging */
94
- debugMode?: boolean;
95
- }
96
-
97
- /**
98
- * Parameters for respond and respondStream methods
99
- */
100
- export interface RespondParams<TContext = unknown, TData = unknown> extends Record<string, unknown> {
101
- history: History;
102
- /**
103
- * The user's message for this turn. When set, the engine appends it to the
104
- * generation history AND to the returned session's history, then appends
105
- * the assistant reply on top — callers who hold sessions no longer need to
106
- * maintain history arrays by hand.
107
- */
108
- message?: string;
109
- /**
110
- * Restrict this turn's ROUTING candidates to these flow ids/titles.
111
- * Directive targets (goTo etc.) still resolve against the full registry.
112
- * Use for entry-pin funnels instead of cloning/filtering agents.
113
- */
114
- allowedFlows?: string[];
115
- step?: StepRef;
116
- session?: SessionState<TData>;
117
- contextOverride?: Partial<TContext>;
118
- signal?: AbortSignal;
119
- }
120
-
121
- /**
122
- * Options for the modern stream() method
123
- */
124
- export interface StreamOptions<TContext = unknown> {
125
- contextOverride?: Partial<TContext>;
126
- signal?: AbortSignal;
127
- history?: History; // Optional: override session history
128
- }
129
-
130
- /**
131
- * Options for the modern generate() method
132
- */
133
- export interface GenerateOptions<TContext = unknown> {
134
- contextOverride?: Partial<TContext>;
135
- signal?: AbortSignal;
136
- history?: History; // Optional: override session history
137
- }
138
-
139
- /**
140
- * Common response context used across all response methods
141
- */
142
- interface ResponseContext<TContext = unknown, TData = unknown> {
143
- effectiveContext: TContext;
144
- session: SessionState<TData>;
145
- history: HistoryItem[]; // Keep as HistoryItem[] for external API compatibility
146
- selectedFlow?: Flow<TContext, TData>;
147
- selectedStep?: Step<TContext, TData>;
148
- responseDirectives?: string[];
149
- isFlowComplete: boolean;
150
- /** AbortSignal for cancellation propagation */
151
- signal?: AbortSignal;
152
- /** Signal firings accumulated across both phases (pre + post) for the response surface. */
153
- signalFirings?: SignalFiring<TContext, TData>[];
154
- /** Pre-phase merged directive from signals (non-position fields like appendPrompt, injectTools). */
155
- signalPreDirective?: Directive<TContext, TData>;
156
- /** Whether the pre-signal phase emitted a halt directive. */
157
- signalHalted?: boolean;
158
- /** Reply from a halt directive. */
159
- signalHaltReply?: string;
160
- /** Flows exited via pendingDirective redirects/resets during routing. */
161
- endedFlows?: EndedFlow[];
162
- /** The user turn supplied via params.message (drives history ownership). */
163
- turnMessage?: string;
164
- }
165
-
166
- /**
167
- * The terminal shape of a turn, decided once by {@link ResponseModal.planTurn}
168
- * and rendered by either the streaming or non-streaming path. Abstracting the
169
- * decision (rather than the rendering) is what keeps the two paths in lockstep.
170
- */
171
- type TurnOutcome<TContext, TData> =
172
- /** No LLM call — emit a verbatim message (signal halt or auto-chain halt). */
173
- | { kind: 'halt'; message: string; stoppedReason: StoppedReason; runPostPhase: boolean }
174
- /** Flow finished — pure state transition, no message of the framework's own. */
175
- | { kind: 'flowComplete'; selectedFlow: Flow<TContext, TData>; stoppedReason: StoppedReason }
176
- /** Render one interactive step via the LLM (the happy path). */
177
- | { kind: 'flowStep'; selectedFlow: Flow<TContext, TData>; step?: Step<TContext, TData>; responseDirectives?: string[]; signalPreDirective?: Directive<TContext, TData> }
178
- /** No flows defined — a simple unstructured response. */
179
- | { kind: 'fallback' };
180
-
181
- /** The output of {@link ResponseModal.planTurn}: the outcome plus shared turn state. */
182
- interface TurnPlan<TContext, TData> {
183
- outcome: TurnOutcome<TContext, TData>;
184
- /** Session after any auto-chain mutation. */
185
- session: SessionState<TData>;
186
- /** Live firing accumulator, seeded with pre-signal phase firings. */
187
- signalFirings: SignalFiring<TContext, TData>[];
188
- effectiveContext: TContext;
189
- history: HistoryItem[];
190
- historyEvents: Event[];
191
- signal?: AbortSignal;
192
- }
193
-
194
- /**
195
- * ResponseModal class that encapsulates all response generation logic
196
- * Uses unified approach for both streaming and non-streaming responses
197
- */
198
- export class ResponseModal<TContext = unknown, TData = unknown> {
199
- private readonly responseEngine: ResponseEngine<TContext, TData>;
200
- private readonly responsePipeline: ResponsePipeline<TContext, TData>;
201
- private readonly stepLifecycle: StepLifecycle<TContext, TData>;
202
- private readonly sessionFinalizer: SessionFinalizer<TContext, TData>;
203
- private readonly toolLoopExecutor: ToolLoopExecutor<TContext, TData>;
204
- private readonly signalCoordinator: SignalCoordinator<TContext, TData>;
205
-
206
- constructor(
207
- private readonly agent: ResponseModalDeps<TContext, TData>,
208
- private readonly options?: ResponseModalOptions
209
- ) {
210
- // Initialize response engine
211
- this.responseEngine = new ResponseEngine<TContext, TData>(this.agent.promptSectionCache);
212
-
213
- // Signal pre/post phase orchestration
214
- this.signalCoordinator = new SignalCoordinator<TContext, TData>({
215
- getFlows: () => this.agent.getFlows(),
216
- signalProcessor: this.agent.signalProcessor,
217
- });
218
-
219
- // Initialize response pipeline with agent dependencies
220
- this.responsePipeline = new ResponsePipeline<TContext, TData>(
221
- this.agent.getAgentOptions(),
222
- () => this.agent.getFlows(), // Pass a function to get flows dynamically
223
- this.agent.getFlowRouter(),
224
- this.signalCoordinator,
225
- this.agent.updateCollectedData.bind(this.agent),
226
- () => this.agent.schema
227
- );
228
-
229
- // Step prepare/finalize execution, shared by the prepare phase and finalizer
230
- this.stepLifecycle = new StepLifecycle<TContext, TData>({
231
- getFlows: () => this.agent.getFlows(),
232
- toolManager: this.getToolManager(),
233
- updateContext: this.agent.updateContext.bind(this.agent),
234
- updateData: this.agent.updateCollectedData.bind(this.agent),
235
- });
236
-
237
- // Single owner of end-of-turn finalization (compaction + persistence + sync)
238
- this.sessionFinalizer = new SessionFinalizer<TContext, TData>({
239
- getCompactionOptions: () => this.agent.getCompactionOptions(),
240
- getPersistenceManager: () => this.agent.getPersistenceManager(),
241
- getAgentOptions: () => this.agent.getAgentOptions(),
242
- getCurrentSession: () => this.agent.currentSession,
243
- setCurrentSession: (session) => { this.agent.currentSession = session; },
244
- stepLifecycle: this.stepLifecycle,
245
- enableAutoSave: this.options?.enableAutoSave,
246
- });
247
-
248
- // Tool follow-up loop (run tools, ask the LLM again) + streaming batch execution
249
- this.toolLoopExecutor = new ToolLoopExecutor<TContext, TData>({
250
- toolManager: this.getToolManager(),
251
- getAgentOptions: () => this.agent.getAgentOptions(),
252
- updateContext: this.agent.updateContext.bind(this.agent),
253
- updateCollectedData: this.agent.updateCollectedData.bind(this.agent),
254
- updateSessionData: this.agent.getUpdateDataMethod(),
255
- maxToolLoops: this.options?.maxToolLoops,
256
- });
257
-
258
- }
259
-
260
- /**
261
- * Generate a non-streaming response using unified logic
262
- */
263
- async respond(params: RespondParams<TContext, TData>): Promise<AgentResponse<TData>> {
264
- // Snapshot the managed session so a failed turn has no in-memory effect:
265
- // without this, mutations made before the failure leave the live session
266
- // diverged from persisted state
267
- const preTurnSession = this.agent.session.current
268
- ? cloneDeep(this.agent.session.current)
269
- : undefined;
270
- try {
271
- // Use unified response preparation and routing
272
- const responseContext = await this.prepareUnifiedResponseContext(params);
273
- // Generate response using unified logic
274
- const result = await this.generateUnifiedResponse(responseContext);
275
-
276
- // Finalize session — the non-streaming turn's single finalize
277
- await this.sessionFinalizer.finalize(result.session!, responseContext.effectiveContext);
278
-
279
- return result;
280
-
281
- } catch (error) {
282
- if (preTurnSession) {
283
- this.agent.session.syncSession(preTurnSession);
284
- }
285
- // Typed library errors carry their own semantics (ProviderError.code,
286
- // SessionConflictError) — rethrow bare so consumers can branch on
287
- // them instead of string-matching. Everything else wraps with the
288
- // original attached as `cause`.
289
- if (error instanceof ProviderError || error instanceof SessionConflictError) {
290
- throw error;
291
- }
292
- throw new ResponseGenerationError(
293
- `[ResponseGenerationError] Response generation failed: ${error instanceof Error ? error.message : String(error)}. ` +
294
- `Check provider configuration and network connectivity.`,
295
- { originalError: error, params, phase: 'response_generation' }
296
- );
297
- }
298
- }
299
-
300
- /**
301
- * Generate a streaming response using unified logic
302
- */
303
- async *respondStream(params: RespondParams<TContext, TData>): AsyncGenerator<AgentResponseStreamChunk<TData>> {
304
- // Same failed-turn rollback semantics as respond()
305
- const preTurnSession = this.agent.session.current
306
- ? cloneDeep(this.agent.session.current)
307
- : undefined;
308
- try {
309
- // Use unified response preparation and routing
310
- const responseContext = await this.prepareUnifiedResponseContext(params);
311
-
312
- // Generate streaming response using unified logic
313
- yield* this.generateUnifiedStreamingResponse(responseContext);
314
-
315
- } catch (error) {
316
- if (preTurnSession) {
317
- this.agent.session.syncSession(preTurnSession);
318
- }
319
- // Stream error to caller
320
- yield {
321
- delta: "",
322
- accumulated: "",
323
- done: true,
324
- session: params.session || await this.agent.session.getOrCreate(),
325
- error: new ResponseGenerationError(
326
- `Streaming response failed: ${error instanceof Error ? error.message : String(error)}`,
327
- { originalError: error, params, phase: 'streaming' }
328
- ),
329
- } as AgentResponseStreamChunk<TData>;
330
- }
331
- }
332
-
333
- /**
334
- * Modern streaming API - simple interface like chat()
335
- */
336
- async *stream(
337
- message?: string,
338
- options?: StreamOptions<TContext>
339
- ): AsyncGenerator<AgentResponseStreamChunk<TData>> {
340
- // Determine which history to use
341
- let history: History;
342
- if (options?.history) {
343
- // Use provided history for this response only
344
- history = options.history;
345
- } else {
346
- // Add user message to session history if provided
347
- if (message) {
348
- await this.agent.session.addMessage("user", message);
349
- }
350
- history = this.agent.session.getHistory();
351
- }
352
-
353
- // Get or create session — session.data is the single source of truth,
354
- // so no agent-side data merge is needed
355
- const session = await this.agent.session.getOrCreate();
356
-
357
- // Stream response using existing respondStream method
358
- let finalMessage = "";
359
- let finalizedSession: SessionState<TData> | undefined;
360
- for await (const chunk of this.respondStream({
361
- history,
362
- session,
363
- contextOverride: options?.contextOverride,
364
- signal: options?.signal,
365
- })) {
366
- // Accumulate the final message and capture finalized session
367
- if (chunk.done) {
368
- finalMessage = chunk.accumulated;
369
- finalizedSession = chunk.session;
370
- }
371
-
372
- yield chunk;
373
- }
374
-
375
- // Sync finalized session to agent.session.current (skip in override-history mode)
376
- // Must happen BEFORE addMessage so the assistant message is added on top of the synced session state
377
- if (!options?.history && finalizedSession) {
378
- this.agent.session.syncSession(finalizedSession);
379
- }
380
-
381
- // Add agent response to session history (only if not using override history)
382
- if (!options?.history && finalMessage) {
383
- await this.agent.session.addMessage("assistant", finalMessage);
384
- }
385
- }
386
-
387
- /**
388
- * Modern non-streaming API - equivalent to chat() but more explicit
389
- */
390
- async generate(
391
- message?: string,
392
- options?: GenerateOptions<TContext>
393
- ): Promise<AgentResponse<TData>> {
394
- // Determine which history to use
395
- let history: History;
396
- if (options?.history) {
397
- // Use provided history for this response only
398
- history = options.history;
399
- } else {
400
- // Add user message to session history if provided
401
- if (message) {
402
- await this.agent.session.addMessage("user", message);
403
- }
404
- history = this.agent.session.getHistory();
405
- }
406
-
407
- // Get or create session — session.data is the single source of truth,
408
- // so no agent-side data merge is needed
409
- const session = await this.agent.session.getOrCreate();
410
-
411
- // Generate response using existing respond method
412
- const result = await this.respond({
413
- history,
414
- session,
415
- contextOverride: options?.contextOverride,
416
- signal: options?.signal,
417
- });
418
-
419
- // Sync finalized session to agent.session.current (skip in override-history mode)
420
- // Must happen BEFORE addMessage so the assistant message is added on top of the synced session state
421
- if (!options?.history && result.session) {
422
- this.agent.session.syncSession(result.session);
423
- }
424
-
425
- // Add agent response to session history (only if not using override history)
426
- if (!options?.history) {
427
- await this.agent.session.addMessage("assistant", result.message);
428
- }
429
-
430
- // Ensure the result includes the current session
431
- return {
432
- ...result,
433
- session: result.session || this.agent.session.current,
434
- };
435
- }
436
-
437
- /**
438
- * Get the response engine instance
439
- * @internal
440
- */
441
- getResponseEngine(): ResponseEngine<TContext, TData> {
442
- return this.responseEngine;
443
- }
444
-
445
- /**
446
- * Get the response pipeline instance
447
- * @internal
448
- */
449
- getResponsePipeline(): ResponsePipeline<TContext, TData> {
450
- return this.responsePipeline;
451
- }
452
-
453
- /**
454
- * Get the ToolManager instance from the agent.
455
- * @private
456
- */
457
- private getToolManager(): ToolManager<TContext, TData> {
458
- return this.agent.tool;
459
- }
460
-
461
- /**
462
- * Recover a structured payload from a schema-mandated response the provider
463
- * could not parse. Raw protocol fragments must never surface as the
464
- * user-visible reply. Three shapes arrive here:
465
- * - a truncated or fence-wrapped envelope → one repair-parse;
466
- * - conversational prose FOLLOWED BY the envelope (the model answered
467
- * twice — the observed WhatsApp leak) → recover the embedded envelope,
468
- * whose "message" field is the complete intended reply;
469
- * - plain prose with nothing recoverable → `undefined`; the caller passes
470
- * it through untouched. (An envelope truncated mid-stream after prose
471
- * also lands here: the prose is user-worthy and the fragment carries
472
- * nothing recoverable.)
473
- * An unrecoverable JSON-SHAPED fragment throws so the turn fails LOUDLY
474
- * and the caller's rollback/retry path engages instead of leaking
475
- * `{"message": "…` to an end user.
476
- */
477
- private salvageStructuredOutput(
478
- raw: string,
479
- surface: "turn" | "stream"
480
- ): AgentStructuredResponse | undefined {
481
- const salvaged = tryParseJSONResponse(raw) as Partial<AgentStructuredResponse> | undefined;
482
- if (salvaged && typeof salvaged.message === "string") {
483
- logger.warn(`[ResponseModal] Salvaged malformed structured output from ${surface} via JSON repair parse.`);
484
- return { ...salvaged, message: salvaged.message };
485
- }
486
- const embedded = extractEmbeddedJSONObject(raw) as Partial<AgentStructuredResponse> | undefined;
487
- if (embedded && typeof embedded.message === "string") {
488
- logger.warn(`[ResponseModal] Salvaged structured output embedded after prose from ${surface}.`);
489
- return { ...embedded, message: embedded.message };
490
- }
491
- if (isJSONShaped(raw)) {
492
- throw ResponseGenerationError.fromError(
493
- new Error(
494
- "Model returned a schema-mandated response that could not be parsed as JSON. " +
495
- `The ${surface} was failed instead of delivering raw protocol output to the user.`
496
- ),
497
- 'structured_output_malformed',
498
- { responseSchemaName: 'response_output' }
499
- );
500
- }
501
- return undefined;
502
- }
503
-
504
- /**
505
- * Tool-emitted directives (ctx.dispatch / `{directive}` returns): state
506
- * writes apply now; control flow queues for the next turn's
507
- * pendingDirective applier (same deferred semantics as dispatch()).
508
- */
509
- private async applyToolEmittedDirectives(
510
- session: SessionState<TData>,
511
- d: Directive<TContext, TData>
512
- ): Promise<SessionState<TData>> {
513
- if (d.dataUpdate) {
514
- session = mergeCollected(session, d.dataUpdate);
515
- }
516
- if (d.contextUpdate) {
517
- await this.agent.updateContext(d.contextUpdate);
518
- }
519
- const control = { ...d };
520
- delete control.dataUpdate;
521
- delete control.contextUpdate;
522
- if (Object.keys(control).length > 0) {
523
- flow.queuePending(session, control);
524
- }
525
- return session;
526
- }
527
-
528
- /**
529
- * Collect scoped instructions from agent, flow, and step into a ScopedInstructions value.
530
- * @private
531
- */
532
- private collectScopedInstructions(
533
- flow?: Flow<TContext, TData>,
534
- step?: Step<TContext, TData>,
535
- ): ScopedInstructions<TContext, TData> {
536
- return {
537
- global: this.agent.instructions,
538
- flow: flow ? { flowTitle: flow.title, items: flow.instructions } : undefined,
539
- step: step ? { stepId: step.id, items: step.getInstructions() } : undefined,
540
- };
541
- }
542
-
543
- // UNIFIED RESPONSE LOGIC - Consolidates common logic between streaming and non-streaming
544
- // ============================================================================
545
-
546
- /**
547
- * Unified response preparation - handles context setup, session management, and routing
548
- * This consolidates common logic between streaming and non-streaming responses
549
- * @private
550
- */
551
- private async prepareUnifiedResponseContext(params: RespondParams<TContext, TData>): Promise<ResponseContext<TContext, TData>> {
552
- try {
553
- const { history: simpleHistory, contextOverride, signal, message: turnMessage, allowedFlows } = params;
554
-
555
- // Validate input parameters
556
- if (!simpleHistory) {
557
- throw new ResponseGenerationError(
558
- '[ResponseGenerationError] Missing history: history is required for response generation. ' +
559
- 'Pass a valid history array (or pass `message` alongside an existing history base).',
560
- { params, phase: 'validation' }
561
- );
562
- }
563
-
564
- // `message` is the user turn: appended to what the model sees this
565
- // turn AND recorded on the returned session's history.
566
- const history = turnMessage
567
- ? [...simpleHistory, userMessage(turnMessage)]
568
- : simpleHistory;
569
-
570
- // Convert HistoryItem[] to Event[] for internal processing
571
- const historyEvents = historyToEvents(history);
572
-
573
- // Use ResponsePipeline for context and session preparation; context
574
- // and session are passed explicitly — the pipeline holds no state
575
- let responseContext: {
576
- effectiveContext: TContext;
577
- session: SessionState<TData>;
578
- contextAfterHook?: TContext;
579
- };
580
- try {
581
- responseContext = await this.responsePipeline.prepareResponseContext({
582
- contextOverride,
583
- session: params.session ? cloneDeep(params.session) : undefined,
584
- currentContext: await this.agent.getContext(),
585
- currentSession: this.agent.currentSession,
586
- });
587
- } catch (error) {
588
- throw ResponseGenerationError.fromError(error, 'pipeline_context_preparation', params);
589
- }
590
-
591
- const { effectiveContext, contextAfterHook } = responseContext;
592
- let session = responseContext.session;
593
-
594
- // Sync the beforeRespond hook's context result back to the agent
595
- if (contextAfterHook !== undefined) {
596
- try {
597
- await this.agent.updateContext(contextAfterHook as Partial<TContext>);
598
- } catch (error) {
599
- throw ResponseGenerationError.fromError(error, 'context_update_from_pipeline', params, { contextAfterHook });
600
- }
601
- }
602
-
603
- // Apply data staged before any session existed (initialData,
604
- // pre-session updateCollectedData calls). Reading the live session's
605
- // data here would leak state across sessions when an explicit
606
- // session is passed, so only the staging buffer is merged.
607
- const stagedData = this.agent.consumePendingData();
608
- if (Object.keys(stagedData).length > 0) {
609
- try {
610
- session = mergeCollected(session, stagedData);
611
- logger.debug("[ResponseModal] Merged staged agent data into session:", stagedData);
612
- } catch (error) {
613
- throw ResponseGenerationError.fromError(error, 'data_merging', params, { stagedData });
614
- }
615
- }
616
-
617
- // Record the user turn on the session's own history so the returned
618
- // session carries the full exchange (respond owns session.history
619
- // when `message` is used).
620
- if (turnMessage) {
621
- session.history = [...(session.history ?? []), userMessage(turnMessage)];
622
- }
623
-
624
- // PHASE 1: PREPARE - Execute prepare function if current step has one
625
- try {
626
- const prepareDirective = await this.stepLifecycle.runPrepare(session, effectiveContext);
627
- // Queue the control-flow directive for THIS turn: routing
628
- // (handleRoutingAndStepSelection) consumes session.pendingDirective
629
- // before deciding flow/step, so a prepare-phase goTo/goToStep/
630
- // reset steers the current turn.
631
- if (prepareDirective) {
632
- flow.queuePending(session, prepareDirective);
633
- }
634
- } catch (error) {
635
- throw ResponseGenerationError.fromError(error, 'step_preparation', params, { session, effectiveContext });
636
- }
637
-
638
- // PHASE 2: ROUTING + STEP SELECTION - Determine which flow and step to use
639
- // Performs pre-extraction and step selection
640
- let routingResult: {
641
- selectedFlow?: Flow<TContext, TData>;
642
- selectedStep?: Step<TContext, TData>;
643
- responseDirectives?: string[];
644
- session: SessionState<TData>;
645
- isFlowComplete: boolean;
646
- signalFirings?: SignalFiring<TContext, TData>[];
647
- signalPreDirective?: Directive<TContext, TData>;
648
- signalHalted?: boolean;
649
- signalHaltReply?: string;
650
- endedFlows?: EndedFlow[];
651
- };
652
- try {
653
- routingResult = await this.responsePipeline.routeAndSelectStep({
654
- session,
655
- history: historyEvents,
656
- context: effectiveContext,
657
- signal,
658
- allowedFlows,
659
- });
660
- } catch (error) {
661
- throw ResponseGenerationError.fromError(error, 'routing_and_step_selection', params, { session, effectiveContext });
662
- }
663
-
664
- return {
665
- effectiveContext,
666
- session: routingResult.session,
667
- history,
668
- turnMessage,
669
- selectedFlow: routingResult.selectedFlow,
670
- selectedStep: routingResult.selectedStep,
671
- responseDirectives: routingResult.responseDirectives,
672
- isFlowComplete: routingResult.isFlowComplete,
673
- signal,
674
- signalFirings: routingResult.signalFirings,
675
- signalPreDirective: routingResult.signalPreDirective,
676
- signalHalted: routingResult.signalHalted,
677
- signalHaltReply: routingResult.signalHaltReply,
678
- endedFlows: routingResult.endedFlows,
679
- };
680
- } catch (error) {
681
- // Re-throw ResponseGenerationError as-is, wrap others
682
- if (ResponseGenerationError.isResponseGenerationError(error)) {
683
- throw error;
684
- }
685
- throw ResponseGenerationError.fromError(error, 'preparation', params);
686
- }
687
- }
688
-
689
- /**
690
- * Plan a turn: run signal-halt detection, the auto-chain walk, and flow/step
691
- * selection, collapsing them into a single {@link TurnOutcome}. This is the
692
- * shared decision spine for both the streaming and non-streaming paths — the
693
- * only logic that genuinely differs between them is how each *renders* the
694
- * outcome (await a value vs. yield chunks) and the leaf provider primitive it
695
- * uses. Centralizing the decision here is what keeps the two paths from
696
- * drifting (the class of bug behind the 2.4.x retry/empty fixes).
697
- *
698
- * The returned `session` reflects any auto-chain mutation; `signalFirings`
699
- * is seeded with the pre-signal phase firings and is the live accumulator the
700
- * post-phase tail appends to.
701
- * @private
702
- */
703
- private async planTurn(
704
- responseContext: ResponseContext<TContext, TData>
705
- ): Promise<TurnPlan<TContext, TData>> {
706
- const {
707
- effectiveContext,
708
- history,
709
- selectedFlow,
710
- selectedStep,
711
- responseDirectives,
712
- isFlowComplete,
713
- signal,
714
- signalFirings: preSignalFirings,
715
- signalPreDirective,
716
- signalHalted,
717
- signalHaltReply,
718
- } = responseContext;
719
- let session = responseContext.session;
720
-
721
- // Accumulator for signal firings across both phases (fire order)
722
- const signalFirings: SignalFiring<TContext, TData>[] = [...(preSignalFirings || [])];
723
- // Convert HistoryItem[] to Event[] for internal processing
724
- const historyEvents = historyToEvents(history);
725
-
726
- const base = { effectiveContext, history, historyEvents, signal, signalFirings };
727
-
728
- // ── SIGNAL HALT (Requirement 8.2) ─────────────────────────────────────
729
- // Pre-signal phase emitted halt → skip LLM call entirely. The post-signal
730
- // phase still runs (it sees the complete turn context).
731
- if (signalHalted) {
732
- const haltMessage = signalHaltReply || '';
733
- return {
734
- ...base, session,
735
- outcome: { kind: 'halt', message: haltMessage, stoppedReason: haltMessage ? 'reply' : 'halt', runPostPhase: true },
736
- };
737
- }
738
-
739
- if (selectedFlow && !isFlowComplete) {
740
- // AUTO-CHAIN: Walk consecutive auto-steps before any LLM work. If the
741
- // current step is auto, the executor advances through it (and any
742
- // subsequent auto-steps) until an interactive step or terminal condition.
743
- let resolvedStep = selectedStep;
744
- const currentStepInstance = session.currentStep
745
- ? selectedFlow.getStep(session.currentStep.id)
746
- : selectedStep;
747
-
748
- if (currentStepInstance?.auto) {
749
- const autoChainExecutor = new AutoChainExecutor<TContext, TData>({
750
- maxAutoStepsPerTurn: this.agent.maxAutoStepsPerTurn,
751
- });
752
- const autoResult: AutoChainResult<TContext, TData> = await autoChainExecutor.run({
753
- session,
754
- context: effectiveContext,
755
- flow: selectedFlow,
756
- });
757
-
758
- session = autoResult.session;
759
-
760
- // Halt: emit the verbatim reply, no LLM call. Unlike signal halt,
761
- // the auto-chain halt is a hard short-circuit that does NOT run the
762
- // post-signal phase (preserved across both paths).
763
- if (autoResult.stoppedReason === 'halt') {
764
- return {
765
- ...base, session,
766
- outcome: { kind: 'halt', message: autoResult.mergedDirective?.reply || '', stoppedReason: 'halt', runPostPhase: false },
767
- };
768
- }
769
-
770
- // Flow completion or cross-flow redirect from auto-chain: the chain
771
- // ended without resolving to an interactive step (last_step: no
772
- // successor; completed: explicit complete; goto: cross-flow redirect).
773
- if (autoResult.stoppedReason === 'last_step' || autoResult.stoppedReason === 'completed' || autoResult.stoppedReason === 'goto') {
774
- logger.debug(`[ResponseModal] Auto-chain ended with ${autoResult.stoppedReason}`);
775
- return {
776
- ...base, session,
777
- outcome: { kind: 'flowComplete', selectedFlow, stoppedReason: autoResult.stoppedReason },
778
- };
779
- }
780
-
781
- // Normal case: auto-chain resolved to an interactive step.
782
- resolvedStep = autoResult.resolvedStep;
783
- }
784
-
785
- return {
786
- ...base, session,
787
- outcome: { kind: 'flowStep', selectedFlow, step: resolvedStep, responseDirectives, signalPreDirective },
788
- };
789
- }
790
-
791
- if (isFlowComplete && selectedFlow) {
792
- // Flow completion path: pure state transition, no LLM call. The reason
793
- // is 'last_step' (implicit terminus — no successor or all skipped).
794
- logger.debug(`[ResponseModal] Releasing session to idle for completed flow: ${selectedFlow.title}`);
795
- return {
796
- ...base, session,
797
- outcome: { kind: 'flowComplete', selectedFlow, stoppedReason: 'last_step' },
798
- };
799
- }
800
-
801
- // Fallback: no flows defined, generate a simple response.
802
- return { ...base, session, outcome: { kind: 'fallback' } };
803
- }
804
-
805
- /**
806
- * The shared post-signal phase tail (Requirement 9.1–9.4). Runs after the
807
- * turn's message is known and before persistence, so post-phase signals see
808
- * the complete turn result (assistant message, collected data, tool results)
809
- * and can override the reply or wire a pendingDirective.
810
- *
811
- * `runPostPhase` is false only for the auto-chain halt short-circuit, which
812
- * deliberately bypasses the post-phase in both paths; that branch still
813
- * surfaces any pre-phase firings via `triggeredSignals`.
814
- * @private
815
- */
816
- private async applyTurnPostPhase(params: {
817
- session: SessionState<TData>;
818
- context: TContext;
819
- historyEvents: Event[];
820
- message: string;
821
- signalFirings: SignalFiring<TContext, TData>[];
822
- runPostPhase: boolean;
823
- }): Promise<{
824
- session: SessionState<TData>;
825
- message: string;
826
- replyOverridden: boolean;
827
- triggeredSignals?: SignalFiring<TContext, TData>[];
828
- }> {
829
- const { session, context, historyEvents, message, signalFirings, runPostPhase } = params;
830
-
831
- if (!runPostPhase) {
832
- return {
833
- session, message, replyOverridden: false,
834
- triggeredSignals: signalFirings.length > 0 ? signalFirings : undefined,
835
- };
836
- }
837
-
838
- const post = await this.signalCoordinator.applyPostPhase({ session, context, historyEvents, message });
839
- signalFirings.push(...post.firings);
840
- return {
841
- session: post.session,
842
- message: post.message,
843
- replyOverridden: post.replyOverridden ?? false,
844
- triggeredSignals: signalFirings.length > 0 ? signalFirings : undefined,
845
- };
846
- }
847
-
848
- /**
849
- * Unified response generation for non-streaming responses.
850
- * Renders the shared {@link planTurn} outcome by awaiting the leaf primitive
851
- * and running the shared post-phase tail; respond() owns the single finalize.
852
- * @private
853
- */
854
- private async generateUnifiedResponse(
855
- responseContext: ResponseContext<TContext, TData>
856
- ): Promise<AgentResponse<TData>> {
857
- const plan = await this.planTurn(responseContext);
858
- const { effectiveContext, history, historyEvents, signal, signalFirings } = plan;
859
- let session = plan.session;
860
-
861
- let message = '';
862
- let toolCalls: Array<{ toolName: string; arguments: Record<string, unknown> }> | undefined = undefined;
863
- let executedSteps: StepRef[] = [];
864
- let stoppedReason: StoppedReason | undefined;
865
- let isFlowComplete = false;
866
- let appliedInstructions: AppliedInstruction[] | undefined;
867
- let runPostPhase = true;
868
- let tokensUsed: number | undefined;
869
- const endedFlows: EndedFlow[] = [...(responseContext.endedFlows ?? [])];
870
-
871
- switch (plan.outcome.kind) {
872
- case 'halt': {
873
- message = plan.outcome.message;
874
- stoppedReason = plan.outcome.stoppedReason;
875
- runPostPhase = plan.outcome.runPostPhase;
876
- break;
877
- }
878
- case 'flowComplete': {
879
- session = await this.applyFlowCompletion({
880
- selectedFlow: plan.outcome.selectedFlow,
881
- session,
882
- context: effectiveContext,
883
- history,
884
- });
885
- isFlowComplete = true;
886
- stoppedReason = plan.outcome.stoppedReason;
887
- endedFlows.push({
888
- flowId: plan.outcome.selectedFlow.id,
889
- title: plan.outcome.selectedFlow.title,
890
- reason: plan.outcome.stoppedReason,
891
- });
892
- break;
893
- }
894
- case 'flowStep': {
895
- const result = await this.processFlowResponse({
896
- selectedFlow: plan.outcome.selectedFlow,
897
- selectedStep: plan.outcome.step,
898
- responseDirectives: plan.outcome.responseDirectives,
899
- session,
900
- history,
901
- context: effectiveContext,
902
- historyEvents,
903
- signal,
904
- // Propagate signal pre-directive's appendPrompt for this turn's LLM call (Requirement 8.4)
905
- transientAppendage: plan.outcome.signalPreDirective?.appendPrompt,
906
- // Merge signal pre-directive (halt/reply/injectTools) into the pre-LLM bus
907
- mergedPreDirective: plan.outcome.signalPreDirective,
908
- });
909
- message = result.message;
910
- toolCalls = result.toolCalls;
911
- session = result.session;
912
- appliedInstructions = result.appliedInstructions;
913
- tokensUsed = result.tokensUsed;
914
- if (plan.outcome.step) {
915
- executedSteps = [{ id: plan.outcome.step.id, flowId: plan.outcome.selectedFlow.id }];
916
- }
917
- // Use stoppedReason from processFlowResponse if set (halt/reply),
918
- // otherwise default to 'needs_input' for normal LLM responses.
919
- stoppedReason = result.stoppedReason || 'needs_input';
920
- break;
921
- }
922
- case 'fallback': {
923
- const fallbackResult = await this.generateFallbackResponse({
924
- history,
925
- context: effectiveContext,
926
- session,
927
- signal,
928
- });
929
- message = fallbackResult.message;
930
- appliedInstructions = fallbackResult.appliedInstructions;
931
- break;
932
- }
933
- }
934
-
935
- const tail = await this.applyTurnPostPhase({
936
- session, context: effectiveContext, historyEvents, message, signalFirings, runPostPhase,
937
- });
938
-
939
- // History ownership: with params.message the returned session carries
940
- // the full exchange — the user turn was recorded pre-routing, the
941
- // assistant tail lands here (post-phase, so overridden replies are the
942
- // ones recorded). Empty tails are skipped.
943
- if (responseContext.turnMessage && tail.message) {
944
- tail.session.history = [...(tail.session.history ?? []), assistantMessage(tail.message)];
945
- }
946
-
947
- return {
948
- message: tail.message,
949
- session: tail.session,
950
- toolCalls,
951
- isFlowComplete,
952
- executedSteps,
953
- stoppedReason,
954
- appliedInstructions,
955
- triggeredSignals: tail.triggeredSignals,
956
- ...(tokensUsed !== undefined ? { metadata: { tokensUsed } } : {}),
957
- ...(endedFlows.length > 0 ? { endedFlows } : {}),
958
- };
959
- }
960
-
961
- /**
962
- * Process flow response with unified tool execution and data collection
963
- * @private
964
- */
965
- private async processFlowResponse(params: {
966
- selectedFlow: Flow<TContext, TData>;
967
- selectedStep?: Step<TContext, TData>;
968
- responseDirectives?: string[];
969
- session: SessionState<TData>;
970
- history: HistoryItem[];
971
- context: TContext;
972
- historyEvents: Event[];
973
- signal?: AbortSignal;
974
- /**
975
- * Per-turn transient appendage from merged directive.appendPrompt.
976
- * Fresh every turn, never cached, never persisted.
977
- */
978
- transientAppendage?: string[];
979
- /**
980
- * Merged directive from the directive bus's pre-LLM phase drain.
981
- * When `halt: true`, the LLM call is skipped entirely.
982
- */
983
- mergedPreDirective?: Directive<TContext, TData>;
984
- }): Promise<{
985
- message: string;
986
- toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
987
- session: SessionState<TData>;
988
- appliedInstructions?: AppliedInstruction[];
989
- stoppedReason?: StoppedReason;
990
- /** Provider-reported usage for the primary generation call. */
991
- tokensUsed?: number;
992
- }> {
993
- const { selectedFlow, selectedStep, responseDirectives, history, context, historyEvents, signal, transientAppendage, mergedPreDirective } = params;
994
- let session = params.session;
995
-
996
- // Resolve the step to render (branches win over linear chain; requires enforced)
997
- const stepResolution = await this.responsePipeline.resolveRenderStep({
998
- selectedFlow,
999
- selectedStep,
1000
- session,
1001
- context,
1002
- });
1003
- if (stepResolution.flowTransition) {
1004
- // Flow transition or completion — no local step to render
1005
- // Return empty message with updated session; caller handles flow transition
1006
- return { message: '', session: stepResolution.session };
1007
- }
1008
- const nextStep = stepResolution.nextStep!;
1009
- session = stepResolution.session;
1010
-
1011
- // Build response schema for this flow (with collect fields from step)
1012
- const responseSchema = this.responseEngine.responseSchemaForFlow(selectedFlow, nextStep, this.agent.schema);
1013
-
1014
- // ── HALT SHORT-CIRCUIT (Requirement 2.5, 2.6, 2.7) ──────────────────────
1015
- // After pre-LLM emissions are merged, if `halt: true` then skip the LLM
1016
- // call entirely. The behavior depends on whether `reply` is also set.
1017
- if (mergedPreDirective?.halt) {
1018
- if (mergedPreDirective.reply) {
1019
- // halt + reply: emit the reply as the assistant message
1020
- logger.debug(`[ResponseModal] Halt with reply — skipping LLM call for step ${nextStep.id}`);
1021
- return { message: mergedPreDirective.reply, session, stoppedReason: 'reply' };
1022
- } else {
1023
- // halt without reply: emit empty assistant content
1024
- logger.debug(`[ResponseModal] Halt without reply — skipping LLM call for step ${nextStep.id}`);
1025
- return { message: '', session, stoppedReason: 'halt' };
1026
- }
1027
- }
1028
-
1029
- // ── STEP.REPLY SHORT-CIRCUIT (Requirement 25.1–25.7, 17.9) ──────────────
1030
- // A step with `reply` set emits a verbatim template response without LLM.
1031
- // onEnter and prepare have already fired normally at this point.
1032
- // If prepare returned a Directive with `reply`, that overrides
1033
- // the step-declared reply (last-emission-wins per Algorithm 4).
1034
- if (nextStep.reply != null) {
1035
- // Determine the effective reply: prepare-emitted reply wins over step-declared
1036
- const effectiveReply = mergedPreDirective?.reply ?? await render(
1037
- nextStep.reply,
1038
- createTemplateContext({ data: session.data || {}, context, session })
1039
- );
1040
- logger.debug(`[ResponseModal] Step.reply — skipping LLM call for step ${nextStep.id}`);
1041
- return { message: effectiveReply, session, stoppedReason: 'reply' };
1042
- }
1043
-
1044
- // Transient appendage: per-turn slot from Directive.appendPrompt.
1045
- // Fresh each turn, never cached, never persisted.
1046
- // Wrapped in try/finally to ensure cleanup even on abnormal termination.
1047
- let turnTransientAppendage: string[] | undefined = transientAppendage;
1048
- try {
1049
- // Build response prompt
1050
- const { prompt: responsePrompt, appliedInstructions } = await this.responseEngine.buildResponsePrompt({
1051
- flow: selectedFlow,
1052
- currentStep: nextStep,
1053
- rules: [],
1054
- prohibitions: [],
1055
- directives: responseDirectives,
1056
- history: historyEvents,
1057
- agentOptions: this.agent.getAgentOptions(),
1058
- instructions: this.collectScopedInstructions(selectedFlow, nextStep),
1059
- combinedTerms: this.agent.getTerms(),
1060
- context,
1061
- session,
1062
- agentSchema: this.agent.schema,
1063
- transientAppendage: turnTransientAppendage,
1064
- });
1065
-
1066
- // Collect available tools for AI
1067
- const availableTools = this.collectAvailableTools(selectedFlow, nextStep);
1068
-
1069
- // Generate message using AI provider
1070
- const agentOptions = this.agent.getAgentOptions();
1071
- const result = await agentOptions.provider.generateMessage({
1072
- prompt: responsePrompt,
1073
- history, // Use HistoryItem[] for AI provider
1074
- context,
1075
- tools: availableTools,
1076
- signal,
1077
- parameters: responseSchema ? { jsonSchema: responseSchema, schemaName: "response_output" } : undefined,
1078
- });
1079
-
1080
- let structuredData = result.structured;
1081
- let message = structuredData?.message || result.message;
1082
- const tokensUsed = result.metadata?.tokensUsed;
1083
-
1084
- // A schema was requested but the provider failed to parse the model's
1085
- // JSON (truncated output, fence-wrapped fragments). Raw protocol
1086
- // fragments must never surface as the user-visible reply: attempt one
1087
- // repair-parse, and if that fails fail the turn LOUDLY so the caller's
1088
- // rollback/retry path engages instead of leaking `{"message": "…` to
1089
- // an end user. Plain prose (not JSON-shaped at all) still passes
1090
- // through — it is not a protocol fragment.
1091
- if (!structuredData && responseSchema && message) {
1092
- const salvaged = this.salvageStructuredOutput(message, "turn");
1093
- if (salvaged) {
1094
- structuredData = salvaged;
1095
- message = salvaged.message;
1096
- }
1097
- }
1098
-
1099
- const effectiveResult = structuredData ? { ...result, structured: structuredData } : result;
1100
- let toolCalls = structuredData?.toolCalls;
1101
-
1102
- // Execute tools with unified loop handling
1103
- const toolResult = await this.toolLoopExecutor.runLoop({
1104
- toolCalls,
1105
- context,
1106
- session,
1107
- history,
1108
- selectedFlow,
1109
- responsePrompt,
1110
- availableTools,
1111
- responseSchema,
1112
- signal,
1113
- });
1114
-
1115
- session = toolResult.session;
1116
- toolCalls = toolResult.finalToolCalls;
1117
- let toolStructured = toolResult.structured;
1118
- if (toolResult.finalMessage) {
1119
- // The tool loop's follow-up calls carry the SAME response schema,
1120
- // and their message replaces the one the guard above already
1121
- // cleared — so an envelope produced after the tools ran reaches
1122
- // the user through a path that guard never sees. (Observed
1123
- // 2026-09-11: a catalog lookup answered, then the closing call
1124
- // returned `{"message": "…"}` raw to a WhatsApp customer.)
1125
- const salvaged = responseSchema
1126
- ? this.salvageStructuredOutput(toolResult.finalMessage, "turn")
1127
- : undefined;
1128
- message = salvaged?.message ?? toolResult.finalMessage;
1129
- if (salvaged) toolStructured = salvaged;
1130
- }
1131
-
1132
- // Tool-emitted directives (ctx.dispatch / `{directive}` returns):
1133
- // state writes apply now; control flow queues for the next turn's
1134
- // pendingDirective applier (same deferred semantics as dispatch()).
1135
- if (toolResult.directives) {
1136
- session = await this.applyToolEmittedDirectives(session, toolResult.directives);
1137
- }
1138
-
1139
- // Collect data from response
1140
- // Use follow-up structured data from tool loop when available, fall back to original result
1141
- const dataSource = toolStructured
1142
- ? { structured: toolStructured }
1143
- : effectiveResult;
1144
- session = await this.collectDataFromResponse({ result: dataSource, selectedFlow, nextStep, session });
1145
-
1146
- return { message, toolCalls, session, appliedInstructions, tokensUsed };
1147
- } finally {
1148
- // Drain the transient appendage at end of turn.
1149
- // This ensures Directive.appendPrompt does not leak to subsequent
1150
- // turns even when the turn terminates abnormally (error, abort, reject).
1151
- turnTransientAppendage = undefined;
1152
- }
1153
- }
1154
-
1155
- /**
1156
- * Unified streaming response generation.
1157
- * Renders the shared {@link planTurn} outcome as a chunk stream and runs the
1158
- * shared post-phase tail on the final chunk (finalizing exactly once).
1159
- * @private
1160
- */
1161
- private async *generateUnifiedStreamingResponse(
1162
- responseContext: ResponseContext<TContext, TData>
1163
- ): AsyncGenerator<AgentResponseStreamChunk<TData>> {
1164
- const plan = await this.planTurn(responseContext);
1165
- const { effectiveContext, history, historyEvents, signal, signalFirings } = plan;
1166
- const session = plan.session;
1167
-
1168
- // Build the inner chunk stream for the planned outcome. `runPostPhase` is
1169
- // the single post-phase gate (false only for auto-chain halt).
1170
- let innerStream: AsyncGenerator<AgentResponseStreamChunk<TData>>;
1171
- let runPostPhase = true;
1172
-
1173
- switch (plan.outcome.kind) {
1174
- case 'halt': {
1175
- runPostPhase = plan.outcome.runPostPhase;
1176
- innerStream = this.streamTerminalMessage({
1177
- message: plan.outcome.message,
1178
- stoppedReason: plan.outcome.stoppedReason,
1179
- session,
1180
- });
1181
- break;
1182
- }
1183
- case 'flowComplete': {
1184
- innerStream = this.streamFlowCompletion({
1185
- selectedFlow: plan.outcome.selectedFlow,
1186
- session,
1187
- context: effectiveContext,
1188
- history,
1189
- historyEvents,
1190
- stoppedReason: plan.outcome.stoppedReason,
1191
- });
1192
- break;
1193
- }
1194
- case 'flowStep': {
1195
- innerStream = this.processFlowStreamingResponse({
1196
- selectedFlow: plan.outcome.selectedFlow,
1197
- selectedStep: plan.outcome.step,
1198
- responseDirectives: plan.outcome.responseDirectives,
1199
- session,
1200
- history,
1201
- context: effectiveContext,
1202
- historyEvents,
1203
- signal,
1204
- transientAppendage: plan.outcome.signalPreDirective?.appendPrompt,
1205
- mergedPreDirective: plan.outcome.signalPreDirective,
1206
- });
1207
- break;
1208
- }
1209
- case 'fallback': {
1210
- innerStream = this.streamFallbackResponse({
1211
- history,
1212
- context: effectiveContext,
1213
- session,
1214
- signal,
1215
- });
1216
- break;
1217
- }
1218
- }
1219
-
1220
- // ── Intercept the inner stream on the final chunk ──────────────────────
1221
- // Mirrors the non-streaming tail: post-signal phase runs first (when
1222
- // applicable), then the session is finalized exactly once, attaching
1223
- // triggeredSignals to the final chunk (Requirement 11.2).
1224
- for await (const chunk of innerStream) {
1225
- if (chunk.done) {
1226
- const tail = await this.applyTurnPostPhase({
1227
- session: chunk.session || session,
1228
- context: effectiveContext,
1229
- historyEvents,
1230
- message: chunk.accumulated,
1231
- signalFirings,
1232
- runPostPhase,
1233
- });
1234
-
1235
- const accumulated = tail.message;
1236
- const delta = tail.replyOverridden ? accumulated : chunk.delta;
1237
-
1238
- // History ownership (parity with the sync tail): with
1239
- // params.message the returned session carries the full
1240
- // exchange — the user turn was recorded pre-routing, the
1241
- // assistant tail lands here, BEFORE finalize persists it.
1242
- if (responseContext.turnMessage && tail.message) {
1243
- tail.session.history = [...(tail.session.history ?? []), assistantMessage(tail.message)];
1244
- }
1245
-
1246
- // Single streaming exit: finalize the post-phase session so
1247
- // post-signal mutations (e.g. pendingDirective) are persisted.
1248
- await this.sessionFinalizer.finalize(tail.session, effectiveContext);
1249
-
1250
- yield {
1251
- ...chunk,
1252
- delta,
1253
- accumulated,
1254
- session: tail.session,
1255
- triggeredSignals: tail.triggeredSignals,
1256
- } as AgentResponseStreamChunk<TData>;
1257
- } else {
1258
- yield chunk;
1259
- }
1260
- }
1261
- }
1262
-
1263
- /**
1264
- * Emit a framework-authored message (a halt reply) as a single terminal
1265
- * chunk, to flow through the shared post-phase tail like any other inner
1266
- * stream. No LLM call, no provider text — so nothing to extract or finalize
1267
- * here; the caller's tail owns post-phase + finalize.
1268
- * @private
1269
- */
1270
- // eslint-disable-next-line @typescript-eslint/require-await -- yield-only async generator; must be `async *` to satisfy the AsyncGenerator return type the caller switches on
1271
- private async *streamTerminalMessage(params: {
1272
- message: string;
1273
- stoppedReason: StoppedReason;
1274
- session: SessionState<TData>;
1275
- }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
1276
- yield {
1277
- delta: params.message,
1278
- accumulated: params.message,
1279
- done: true,
1280
- session: params.session,
1281
- toolCalls: undefined,
1282
- isFlowComplete: false,
1283
- stoppedReason: params.stoppedReason,
1284
- executedSteps: [],
1285
- } as AgentResponseStreamChunk<TData>;
1286
- }
1287
-
1288
- /**
1289
- * Wrap a provider message stream so each chunk's `delta`/`accumulated` carry
1290
- * clean message text instead of the raw structured-JSON wrapper. The single
1291
- * point where streamed JSON is unwrapped — every streaming response variant
1292
- * (flow step, fallback) consumes provider chunks through here, so consumers
1293
- * and stored history never see `{"message":...}` fragments. `structured`,
1294
- * `done`, and `metadata` pass through untouched.
1295
- * @private
1296
- */
1297
- private async *decodeMessageStream(
1298
- stream: AsyncGenerator<GenerateMessageStreamChunk<AgentStructuredResponse>>
1299
- ): AsyncGenerator<GenerateMessageStreamChunk<AgentStructuredResponse>> {
1300
- const decoder = new StreamingMessageDecoder();
1301
- for await (const chunk of stream) {
1302
- const clean = decoder.push(chunk.accumulated);
1303
- yield { ...chunk, delta: clean.delta, accumulated: clean.message };
1304
- }
1305
- }
1306
-
1307
- /**
1308
- * Process flow streaming response with unified tool execution and data collection
1309
- * @private
1310
- */
1311
- private async *processFlowStreamingResponse(params: {
1312
- selectedFlow: Flow<TContext, TData>;
1313
- selectedStep?: Step<TContext, TData>;
1314
- responseDirectives?: string[];
1315
- session: SessionState<TData>;
1316
- history: HistoryItem[];
1317
- context: TContext;
1318
- historyEvents: Event[];
1319
- signal?: AbortSignal;
1320
- /**
1321
- * Per-turn transient appendage from merged directive.appendPrompt.
1322
- * Fresh every turn, never cached, never persisted.
1323
- */
1324
- transientAppendage?: string[];
1325
- /**
1326
- * Merged directive from the directive bus's pre-LLM phase drain.
1327
- * When `halt: true`, the LLM call is skipped entirely.
1328
- */
1329
- mergedPreDirective?: Directive<TContext, TData>;
1330
- }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
1331
- const { selectedFlow, selectedStep, responseDirectives, history, context, historyEvents, signal, transientAppendage, mergedPreDirective } = params;
1332
- let session = params.session;
1333
-
1334
- // Resolve the step to render (same logic as non-streaming)
1335
- const stepResolution = await this.responsePipeline.resolveRenderStep({
1336
- selectedFlow,
1337
- selectedStep,
1338
- session,
1339
- context,
1340
- });
1341
- if (stepResolution.flowTransition) {
1342
- // Flow transition or completion — no step to render
1343
- yield {
1344
- delta: '',
1345
- accumulated: '',
1346
- done: true,
1347
- session: stepResolution.session,
1348
- } as AgentResponseStreamChunk<TData>;
1349
- return;
1350
- }
1351
- const nextStep = stepResolution.nextStep!;
1352
- session = stepResolution.session;
1353
-
1354
- // Build response schema and prompt (same as non-streaming)
1355
- const responseSchema = this.responseEngine.responseSchemaForFlow(selectedFlow, nextStep, this.agent.schema);
1356
-
1357
- // ── HALT SHORT-CIRCUIT (Requirement 2.5, 2.6, 2.7) ──────────────────────
1358
- // After pre-LLM emissions are merged, if `halt: true` then skip the LLM
1359
- // call entirely. Emit a single done chunk with the appropriate content.
1360
- if (mergedPreDirective?.halt) {
1361
- const reply = mergedPreDirective.reply || '';
1362
- const reason: StoppedReason = mergedPreDirective.reply ? 'reply' : 'halt';
1363
- logger.debug(`[ResponseModal] Halt (streaming) — skipping LLM call for step ${nextStep.id}, stoppedReason: ${reason}`);
1364
- yield {
1365
- delta: reply,
1366
- accumulated: reply,
1367
- done: true,
1368
- session,
1369
- stoppedReason: reason,
1370
- executedSteps: [{ id: nextStep.id, flowId: selectedFlow.id }],
1371
- } as AgentResponseStreamChunk<TData>;
1372
- return;
1373
- }
1374
-
1375
- // ── STEP.REPLY SHORT-CIRCUIT (Requirement 25.1–25.7, 17.9) ──────────────
1376
- // A step with `reply` set emits a verbatim template response without LLM.
1377
- // onEnter and prepare have already fired normally. If prepare returned
1378
- // a Directive with `reply`, that overrides the step-declared reply.
1379
- if (nextStep.reply != null) {
1380
- const effectiveReply = mergedPreDirective?.reply ?? await render(
1381
- nextStep.reply,
1382
- createTemplateContext({ data: session.data || {}, context, session })
1383
- );
1384
- logger.debug(`[ResponseModal] Step.reply (streaming) — skipping LLM call for step ${nextStep.id}`);
1385
- yield {
1386
- delta: effectiveReply,
1387
- accumulated: effectiveReply,
1388
- done: true,
1389
- session,
1390
- stoppedReason: 'reply',
1391
- executedSteps: [{ id: nextStep.id, flowId: selectedFlow.id }],
1392
- } as AgentResponseStreamChunk<TData>;
1393
- return;
1394
- }
1395
-
1396
- // Transient appendage: per-turn slot from Directive.appendPrompt.
1397
- // Fresh each turn, never cached, never persisted.
1398
- // Wrapped in try/finally to ensure cleanup even on abnormal termination.
1399
- let turnTransientAppendage: string[] | undefined = transientAppendage;
1400
- try {
1401
- const { prompt: responsePrompt, appliedInstructions } = await this.responseEngine.buildResponsePrompt({
1402
- flow: selectedFlow,
1403
- currentStep: nextStep,
1404
- rules: [],
1405
- prohibitions: [],
1406
- directives: responseDirectives,
1407
- history: historyEvents,
1408
- agentOptions: this.agent.getAgentOptions(),
1409
- instructions: this.collectScopedInstructions(selectedFlow, nextStep),
1410
- combinedTerms: this.agent.getTerms(),
1411
- context,
1412
- session,
1413
- agentSchema: this.agent.schema,
1414
- transientAppendage: turnTransientAppendage,
1415
- });
1416
-
1417
- // Collect available tools for AI
1418
- const availableTools = this.collectAvailableTools(selectedFlow, nextStep);
1419
-
1420
- // Generate message stream using AI provider
1421
- const agentOptions = this.agent.getAgentOptions();
1422
- const stream = agentOptions.provider.generateMessageStream({
1423
- prompt: responsePrompt,
1424
- history, // Use HistoryItem[] for AI provider
1425
- context,
1426
- tools: availableTools,
1427
- signal,
1428
- parameters: { jsonSchema: responseSchema, schemaName: "response_stream_output" },
1429
- });
1430
-
1431
- // Stream chunks with unified tool handling. decodeMessageStream gives
1432
- // each chunk clean message text in delta/accumulated, so the non-done
1433
- // deltas, the final accumulated, the post-phase message input, and the
1434
- // assistant message stored by stream() are all clean — never the raw
1435
- // JSON wrapper (matching the non-streaming structured.message extraction).
1436
- for await (const chunk of this.decodeMessageStream(stream)) {
1437
- let toolCalls: Array<{ toolName: string; arguments: Record<string, unknown> }> | undefined = undefined;
1438
- // Final message/structured may be replaced by a forced post-tool
1439
- // response (see runStreamingBatch / gap: tools-ran-but-no-text).
1440
- let finalDelta = chunk.delta;
1441
- let finalAccumulated = chunk.accumulated;
1442
- let finalStructured = chunk.structured;
1443
-
1444
- // Extract tool calls from AI response on final chunk
1445
- if (chunk.done && chunk.structured?.toolCalls) {
1446
- toolCalls = chunk.structured.toolCalls;
1447
-
1448
- // Concurrent execution for the initial batch of tool calls,
1449
- // yielding tool-progress chunks as they arrive. The accumulated
1450
- // preamble is already clean text.
1451
- const batchResult = yield* this.toolLoopExecutor.runStreamingBatch({
1452
- toolCalls,
1453
- context,
1454
- session,
1455
- history,
1456
- selectedFlow,
1457
- step: nextStep,
1458
- accumulated: chunk.accumulated,
1459
- responsePrompt,
1460
- availableTools,
1461
- responseSchema,
1462
- signal,
1463
- });
1464
- session = batchResult.session;
1465
- toolCalls = batchResult.toolCalls;
1466
-
1467
- // Tool-emitted directives (ctx.dispatch / `{directive}`):
1468
- // state writes apply now; control flow queues for the next
1469
- // turn's pendingDirective applier (same deferred semantics
1470
- // as dispatch()). A verbatim tool reply already replaced the
1471
- // closing message inside runStreamingBatch.
1472
- if (batchResult.directives) {
1473
- session = await this.applyToolEmittedDirectives(session, batchResult.directives);
1474
- }
1475
-
1476
- // Prefer the post-tool follow-up structured for collection and
1477
- // emission whenever present — independent of whether a closing
1478
- // message was forced — matching the non-streaming path's
1479
- // `toolResult.structured ?? result` selection.
1480
- finalStructured = batchResult.structured ?? finalStructured;
1481
-
1482
- // Tools ran but the model produced no result-aware text — use
1483
- // the forced closing message (already clean) so we never emit the
1484
- // bare preamble (or an empty message) as the final response. Its
1485
- // delta is the portion not already streamed as the preamble.
1486
- if (batchResult.finalMessage) {
1487
- finalAccumulated = batchResult.finalMessage;
1488
- finalDelta = batchResult.finalMessage.startsWith(chunk.accumulated)
1489
- ? batchResult.finalMessage.slice(chunk.accumulated.length)
1490
- : batchResult.finalMessage;
1491
- }
1492
- }
1493
-
1494
- // Streaming twin of the non-streaming salvage guard: a schema was
1495
- // requested but no structured payload arrived, and the accumulated
1496
- // text is JSON-shaped (a protocol fragment) — repair-parse it or
1497
- // fail the turn rather than leaking raw output to the user.
1498
- if (chunk.done && !finalStructured && responseSchema && finalAccumulated) {
1499
- const salvaged = this.salvageStructuredOutput(finalAccumulated, "stream");
1500
- if (salvaged) {
1501
- finalDelta = salvaged.message.startsWith(finalAccumulated)
1502
- ? salvaged.message.slice(finalAccumulated.length)
1503
- : salvaged.message;
1504
- finalStructured = salvaged;
1505
- finalAccumulated = salvaged.message;
1506
- }
1507
- }
1508
-
1509
- // Collect data on the final chunk for any flow step — flow
1510
- // required/optional fields are valid targets even without a step
1511
- // `collect` — preferring the post-tool follow-up structured so a
1512
- // tool-driven turn harvests fields the model produced after tools.
1513
- if (chunk.done && finalStructured) {
1514
- session = await this.collectDataFromResponse({
1515
- result: { structured: finalStructured },
1516
- selectedFlow,
1517
- nextStep,
1518
- session,
1519
- });
1520
- }
1521
-
1522
- // Response structure completeness (Requirement 8.1, 8.2, 8.3)
1523
- // - executedSteps: single step executed in this response
1524
- // - stoppedReason: 'needs_input' for single-step execution (waiting for user input)
1525
- // - session.currentStep: reflects the executed step
1526
- yield {
1527
- delta: finalDelta,
1528
- accumulated: finalAccumulated,
1529
- done: chunk.done,
1530
- session,
1531
- toolCalls,
1532
- isFlowComplete: false,
1533
- executedSteps: chunk.done ? [{ id: nextStep.id, flowId: selectedFlow.id }] : undefined,
1534
- stoppedReason: chunk.done ? 'needs_input' : undefined,
1535
- metadata: chunk.metadata,
1536
- structured: finalStructured,
1537
- appliedInstructions: chunk.done ? appliedInstructions : undefined,
1538
- };
1539
- }
1540
- } finally {
1541
- // Drain the transient appendage at end of turn.
1542
- // This ensures Directive.appendPrompt does not leak to subsequent
1543
- // turns even when the turn terminates abnormally (error, abort, reject).
1544
- turnTransientAppendage = undefined;
1545
- }
1546
- }
1547
-
1548
- /**
1549
- * Unified data collection from AI response
1550
- * @private
1551
- */
1552
- private async collectDataFromResponse(params: {
1553
- result: { structured?: AgentStructuredResponse };
1554
- selectedFlow?: Flow<TContext, TData>;
1555
- nextStep?: Step<TContext, TData>;
1556
- session: SessionState<TData>;
1557
- }): Promise<SessionState<TData>> {
1558
- try {
1559
- const { result, selectedFlow, nextStep, session } = params;
1560
- let updatedSession = session;
1561
-
1562
- // Extract collected data from final response (only for flow-based interactions)
1563
- if (selectedFlow && result.structured) {
1564
- try {
1565
- const collectedData: Record<string, unknown> = {};
1566
- // AgentStructuredResponse extends Record<string, unknown>, so we can safely access properties
1567
- const structuredData = result.structured;
1568
-
1569
- // Collect ALL flow fields (required + optional) from structured response
1570
- const allFlowFields = new Set<string>();
1571
-
1572
- // Add flow required fields
1573
- if (selectedFlow.requiredFields) {
1574
- selectedFlow.requiredFields.forEach(field => allFlowFields.add(String(field)));
1575
- }
1576
-
1577
- // Add flow optional fields
1578
- if (selectedFlow.optionalFields) {
1579
- selectedFlow.optionalFields.forEach(field => allFlowFields.add(String(field)));
1580
- }
1581
-
1582
- // Also include current step's collect fields (in case they're not in flow fields)
1583
- if (nextStep?.collect) {
1584
- nextStep.collect.forEach(field => allFlowFields.add(String(field)));
1585
- }
1586
-
1587
- // Extract all available fields from structured response
1588
- for (const field of allFlowFields) {
1589
- const fieldKey = String(field);
1590
- if (fieldKey in structuredData && structuredData[fieldKey] !== undefined && structuredData[fieldKey] !== null) {
1591
- collectedData[fieldKey] = structuredData[fieldKey];
1592
- }
1593
- }
1594
-
1595
- // Merge collected data into session using agent-level data validation
1596
- if (Object.keys(collectedData).length > 0) {
1597
- try {
1598
- // Update agent-level collected data with validation
1599
- await this.agent.updateCollectedData(collectedData as Partial<TData>);
1600
-
1601
- // Update session with validated data
1602
- const updateDataMethod = this.agent.getUpdateDataMethod();
1603
- updatedSession = await updateDataMethod(updatedSession, collectedData as Partial<TData>);
1604
- logger.debug(`[ResponseModal] Collected data:`, collectedData);
1605
- } catch (error) {
1606
- logger.error(`[ResponseModal] Failed to update collected data:`, error);
1607
- // Continue without updating data rather than failing completely
1608
- }
1609
- }
1610
- } catch (error) {
1611
- logger.error(`[ResponseModal] Error during data collection:`, error);
1612
- // Continue without collecting data rather than failing completely
1613
- }
1614
- }
1615
-
1616
- // Extract any additional data from structured response
1617
- // Since AgentStructuredResponse extends Record<string, unknown>, we can safely check for additional properties
1618
- if (result.structured && "contextUpdate" in result.structured) {
1619
- try {
1620
- const contextUpdate = (result.structured as AgentStructuredResponse & { contextUpdate?: Partial<TContext> }).contextUpdate;
1621
- if (contextUpdate) {
1622
- await this.agent.updateContext(contextUpdate);
1623
- }
1624
- } catch (error) {
1625
- logger.error(`[ResponseModal] Failed to update context from structured response:`, error);
1626
- // Continue without updating context rather than failing completely
1627
- }
1628
- }
1629
-
1630
- return updatedSession;
1631
- } catch (error) {
1632
- logger.error(`[ResponseModal] Error in collectDataFromResponse:`, error);
1633
- // Return original session if data collection fails completely
1634
- return params.session;
1635
- }
1636
- }
1637
-
1638
- /**
1639
- * Apply flow completion: release the session to idle state.
1640
- *
1641
- * This is a pure state transition. The framework emits **no message of
1642
- * its own** at the completion boundary — every word delivered to the
1643
- * user comes from a developer-defined step prompt. If the dev wants a
1644
- * closing turn, they add a final interactive step with their own
1645
- * `prompt`; the framework respects that step's natural LLM output.
1646
- *
1647
- * Behavior:
1648
- * - Marks the active `flowHistory` entry as `completed: true` and
1649
- * stamps `exitedAt`.
1650
- * - Evaluates `flow.onComplete` for an explicit follow-up transition.
1651
- * When set, populates `session.pendingDirective` (the next turn's
1652
- * pipeline applies it). When absent, the session is fully idle.
1653
- * - Clears `currentFlow` and `currentStep` to `undefined`.
1654
- * - Clears owned fields when the flow is `reentrant` so subsequent
1655
- * re-selections start from a clean state.
1656
- *
1657
- * Returns the updated session. Callers compose any reply text from
1658
- * their own sources (an upstream LLM turn, a directive's `reply`, or
1659
- * an empty string for silent completion).
1660
- *
1661
- * @private
1662
- */
1663
- private async applyFlowCompletion(params: {
1664
- selectedFlow: Flow<TContext, TData>;
1665
- session: SessionState<TData>;
1666
- context: TContext;
1667
- history: HistoryItem[];
1668
- }): Promise<SessionState<TData>> {
1669
- const { selectedFlow, session, context } = params;
1670
-
1671
- // 1) Evaluate onComplete first — needs the still-active session shape.
1672
- const transitionConfig = await selectedFlow.evaluateOnComplete(
1673
- { data: session.data },
1674
- context,
1675
- );
1676
-
1677
- // 2) Release to idle. If the flow is reentrant, scrub its owned
1678
- // fields so re-selection on a future turn starts clean. When
1679
- // onComplete fires we still go idle here — the next turn's
1680
- // pipeline applies the pendingDirective before any routing.
1681
- const ownedFields = selectedFlow.reentrant
1682
- ? [
1683
- ...(selectedFlow.requiredFields ?? []),
1684
- ...(selectedFlow.optionalFields ?? []),
1685
- ]
1686
- : undefined;
1687
-
1688
- let nextSession = completeCurrentFlow(session, {
1689
- clearOwnedFields: ownedFields,
1690
- });
1691
-
1692
- // 3) Wire pendingDirective when onComplete returned a target.
1693
- if (transitionConfig) {
1694
- const goToTarget = typeof transitionConfig.goTo === 'string'
1695
- ? transitionConfig.goTo
1696
- : transitionConfig.goTo?.flow;
1697
-
1698
- const targetFlow = goToTarget ? this.agent.getFlows().find(
1699
- (r) =>
1700
- r.id === goToTarget ||
1701
- r.title === goToTarget,
1702
- ) : undefined;
1703
-
1704
- if (targetFlow) {
1705
- nextSession = {
1706
- ...nextSession,
1707
- pendingDirective: {
1708
- goTo: targetFlow.id,
1709
- },
1710
- };
1711
- logger.debug(
1712
- `[ResponseModal] Flow ${selectedFlow.title} completed with pending directive to: ${targetFlow.title}`,
1713
- );
1714
- } else if (goToTarget) {
1715
- logger.warn(
1716
- `[FlowConfigurationError] onComplete target not found: flow "${selectedFlow.title}" completed but onComplete target "${goToTarget}" does not match any flow. ` +
1717
- `Fix the onComplete value to reference an existing flow id/title, or remove onComplete to release the session to idle.`,
1718
- );
1719
- }
1720
- } else {
1721
- logger.debug(
1722
- `[ResponseModal] Flow ${selectedFlow.title} completed; session released to idle.`,
1723
- );
1724
- }
1725
-
1726
- return nextSession;
1727
- }
1728
-
1729
- /**
1730
- * Stream a flow completion as a single terminal chunk.
1731
- *
1732
- * No LLM call is made. The framework no longer authors a farewell — the
1733
- * completion path is a pure state transition. The chunk emits an empty
1734
- * `delta` and a `done: true` flag with the idle session attached so
1735
- * downstream consumers can finalize cleanly.
1736
- *
1737
- * If the developer wants closing copy in a streaming response, they
1738
- * should add a final interactive step whose own LLM turn delivers it.
1739
- *
1740
- * @private
1741
- */
1742
- private async *streamFlowCompletion(params: {
1743
- selectedFlow: Flow<TContext, TData>;
1744
- session: SessionState<TData>;
1745
- context: TContext;
1746
- history: HistoryItem[];
1747
- historyEvents: Event[];
1748
- stoppedReason?: StoppedReason;
1749
- signal?: AbortSignal;
1750
- }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
1751
- const { selectedFlow, context, history } = params;
1752
-
1753
- const session = await this.applyFlowCompletion({
1754
- selectedFlow,
1755
- session: params.session,
1756
- context,
1757
- history,
1758
- });
1759
-
1760
- yield {
1761
- delta: '',
1762
- accumulated: '',
1763
- done: true,
1764
- session,
1765
- toolCalls: undefined,
1766
- isFlowComplete: true,
1767
- executedSteps: [],
1768
- stoppedReason: params.stoppedReason ?? 'completed',
1769
- };
1770
- }
1771
-
1772
- /**
1773
- * Generate fallback response when no flows are available
1774
- * @private
1775
- */
1776
- private async generateFallbackResponse(params: {
1777
- history: HistoryItem[];
1778
- context: TContext;
1779
- session: SessionState<TData>;
1780
- signal?: AbortSignal;
1781
- }): Promise<{ message: string; appliedInstructions?: AppliedInstruction[] }> {
1782
- const { history, context, session, signal } = params;
1783
-
1784
- logger.debug(`[ResponseModal] No flow selected, generating basic response`);
1785
-
1786
- // Build basic response prompt without flow context
1787
- const { prompt: fallbackPrompt, appliedInstructions } = await this.responseEngine.buildFallbackPrompt({
1788
- agentOptions: this.agent.getAgentOptions(),
1789
- terms: this.agent.getTerms(),
1790
- instructions: this.collectScopedInstructions(),
1791
- context,
1792
- session,
1793
- });
1794
-
1795
- const agentOptions = this.agent.getAgentOptions();
1796
- const result = await agentOptions.provider.generateMessage({
1797
- prompt: fallbackPrompt,
1798
- history,
1799
- context,
1800
- signal,
1801
- parameters: {
1802
- jsonSchema: {
1803
- type: "object",
1804
- properties: { message: { type: "string" } },
1805
- required: ["message"],
1806
- additionalProperties: false,
1807
- },
1808
- schemaName: "fallback_response",
1809
- },
1810
- });
1811
-
1812
- return { message: result.structured?.message || result.message, appliedInstructions };
1813
- }
1814
-
1815
- /**
1816
- * Stream fallback response when no flows are available
1817
- * @private
1818
- */
1819
- private async *streamFallbackResponse(params: {
1820
- history: HistoryItem[];
1821
- context: TContext;
1822
- session: SessionState<TData>;
1823
- signal?: AbortSignal;
1824
- }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
1825
- const { history, context, session, signal } = params;
1826
-
1827
- const { prompt: fallbackPrompt, appliedInstructions } = await this.responseEngine.buildFallbackPrompt({
1828
- agentOptions: this.agent.getAgentOptions(),
1829
- terms: this.agent.getTerms(),
1830
- instructions: this.collectScopedInstructions(),
1831
- context,
1832
- session,
1833
- });
1834
-
1835
- const agentOptions = this.agent.getAgentOptions();
1836
- const stream = agentOptions.provider.generateMessageStream({
1837
- prompt: fallbackPrompt,
1838
- history,
1839
- context,
1840
- signal,
1841
- parameters: {
1842
- jsonSchema: {
1843
- type: "object",
1844
- properties: { message: { type: "string" } },
1845
- required: ["message"],
1846
- additionalProperties: false,
1847
- },
1848
- schemaName: "fallback_stream_response",
1849
- },
1850
- });
1851
-
1852
- // Decode the JSON wrapper to clean message text (same as the flow path).
1853
- for await (const chunk of this.decodeMessageStream(stream)) {
1854
- // Response structure completeness (Requirement 8.1, 8.2, 8.3)
1855
- // - executedSteps: empty for fallback (no flow/step execution)
1856
- // - stoppedReason: undefined for fallback (no flow context)
1857
- // - session.currentStep: unchanged (no step progression)
1858
- yield {
1859
- delta: chunk.delta,
1860
- accumulated: chunk.accumulated,
1861
- done: chunk.done,
1862
- session,
1863
- toolCalls: undefined,
1864
- isFlowComplete: false,
1865
- executedSteps: chunk.done ? [] : undefined,
1866
- stoppedReason: undefined,
1867
- metadata: chunk.metadata,
1868
- structured: chunk.structured,
1869
- appliedInstructions: chunk.done ? appliedInstructions : undefined,
1870
- };
1871
- }
1872
- }
1873
-
1874
- // ============================================================================
1875
- // UTILITY METHODS - Helper methods for tool management and other utilities
1876
- // ============================================================================
1877
-
1878
-
1879
- /**
1880
- * Collect all available tools for the given flow and step context.
1881
- * Delegates to ToolManager for unified tool resolution and deduplication.
1882
- * @private
1883
- */
1884
- private collectAvailableTools(
1885
- flow?: Flow<TContext, TData>,
1886
- step?: Step<TContext, TData>
1887
- ): Array<{
1888
- id: string;
1889
- name: string;
1890
- description?: string;
1891
- parameters?: unknown;
1892
- }> {
1893
- const availableTools = this.getToolManager().getAvailable(undefined, step, flow);
1894
- return availableTools.map((tool) => ({
1895
- id: tool.id,
1896
- name: tool.id,
1897
- description: tool.description,
1898
- parameters: tool.parameters,
1899
- }));
1900
- }
1901
-
1902
- }