@falai/agent 0.8.1 → 0.9.0-alpha-1

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 (652) hide show
  1. package/README.md +306 -133
  2. package/dist/{adapters → cjs/src/adapters}/MemoryAdapter.d.ts +4 -4
  3. package/dist/cjs/src/adapters/MemoryAdapter.d.ts.map +1 -0
  4. package/dist/cjs/{adapters → src/adapters}/MemoryAdapter.js +41 -21
  5. package/dist/cjs/src/adapters/MemoryAdapter.js.map +1 -0
  6. package/dist/{adapters → cjs/src/adapters}/MongoAdapter.d.ts +3 -3
  7. package/dist/cjs/src/adapters/MongoAdapter.d.ts.map +1 -0
  8. package/dist/cjs/{adapters → src/adapters}/MongoAdapter.js +2 -1
  9. package/dist/cjs/src/adapters/MongoAdapter.js.map +1 -0
  10. package/dist/cjs/{adapters → src/adapters}/OpenSearchAdapter.d.ts +3 -3
  11. package/dist/cjs/src/adapters/OpenSearchAdapter.d.ts.map +1 -0
  12. package/dist/cjs/{adapters → src/adapters}/OpenSearchAdapter.js +10 -13
  13. package/dist/cjs/src/adapters/OpenSearchAdapter.js.map +1 -0
  14. package/dist/cjs/{adapters → src/adapters}/PostgreSQLAdapter.d.ts +3 -3
  15. package/dist/cjs/src/adapters/PostgreSQLAdapter.d.ts.map +1 -0
  16. package/dist/cjs/{adapters → src/adapters}/PostgreSQLAdapter.js +1 -1
  17. package/dist/cjs/src/adapters/PostgreSQLAdapter.js.map +1 -0
  18. package/dist/cjs/{adapters → src/adapters}/PrismaAdapter.d.ts +3 -3
  19. package/dist/cjs/src/adapters/PrismaAdapter.d.ts.map +1 -0
  20. package/dist/cjs/{adapters → src/adapters}/PrismaAdapter.js +35 -5
  21. package/dist/cjs/src/adapters/PrismaAdapter.js.map +1 -0
  22. package/dist/cjs/{adapters → src/adapters}/RedisAdapter.d.ts +3 -3
  23. package/dist/cjs/src/adapters/RedisAdapter.d.ts.map +1 -0
  24. package/dist/cjs/{adapters → src/adapters}/RedisAdapter.js +3 -2
  25. package/dist/cjs/src/adapters/RedisAdapter.js.map +1 -0
  26. package/dist/{adapters → cjs/src/adapters}/SQLiteAdapter.d.ts +3 -3
  27. package/dist/cjs/src/adapters/SQLiteAdapter.d.ts.map +1 -0
  28. package/dist/cjs/{adapters → src/adapters}/SQLiteAdapter.js +2 -1
  29. package/dist/cjs/src/adapters/SQLiteAdapter.js.map +1 -0
  30. package/dist/cjs/src/adapters/index.d.ts.map +1 -0
  31. package/dist/cjs/src/adapters/index.js.map +1 -0
  32. package/dist/cjs/src/constants/index.d.ts.map +1 -0
  33. package/dist/cjs/src/constants/index.js.map +1 -0
  34. package/dist/{core → cjs/src/core}/Agent.d.ts +65 -67
  35. package/dist/cjs/src/core/Agent.d.ts.map +1 -0
  36. package/dist/cjs/src/core/Agent.js +1433 -0
  37. package/dist/cjs/src/core/Agent.js.map +1 -0
  38. package/dist/cjs/src/core/Events.d.ts +26 -0
  39. package/dist/cjs/src/core/Events.d.ts.map +1 -0
  40. package/dist/cjs/src/core/Events.js +144 -0
  41. package/dist/cjs/src/core/Events.js.map +1 -0
  42. package/dist/{core → cjs/src/core}/PersistenceManager.d.ts +21 -19
  43. package/dist/cjs/src/core/PersistenceManager.d.ts.map +1 -0
  44. package/dist/cjs/{core → src/core}/PersistenceManager.js +50 -20
  45. package/dist/cjs/src/core/PersistenceManager.js.map +1 -0
  46. package/dist/cjs/src/core/PromptComposer.d.ts +27 -0
  47. package/dist/cjs/src/core/PromptComposer.d.ts.map +1 -0
  48. package/dist/cjs/src/core/PromptComposer.js +157 -0
  49. package/dist/cjs/src/core/PromptComposer.js.map +1 -0
  50. package/dist/cjs/src/core/ResponseEngine.d.ts +31 -0
  51. package/dist/cjs/src/core/ResponseEngine.d.ts.map +1 -0
  52. package/dist/cjs/src/core/ResponseEngine.js +84 -0
  53. package/dist/cjs/src/core/ResponseEngine.js.map +1 -0
  54. package/dist/cjs/src/core/ResponsePipeline.d.ts +143 -0
  55. package/dist/cjs/src/core/ResponsePipeline.d.ts.map +1 -0
  56. package/dist/cjs/src/core/ResponsePipeline.js +446 -0
  57. package/dist/cjs/src/core/ResponsePipeline.js.map +1 -0
  58. package/dist/cjs/src/core/Route.d.ts +126 -0
  59. package/dist/cjs/src/core/Route.d.ts.map +1 -0
  60. package/dist/cjs/{core → src/core}/Route.js +116 -20
  61. package/dist/cjs/src/core/Route.js.map +1 -0
  62. package/dist/{core → cjs/src/core}/RoutingEngine.d.ts +33 -38
  63. package/dist/cjs/src/core/RoutingEngine.d.ts.map +1 -0
  64. package/dist/cjs/{core → src/core}/RoutingEngine.js +102 -108
  65. package/dist/cjs/src/core/RoutingEngine.js.map +1 -0
  66. package/dist/cjs/src/core/SessionManager.d.ts +76 -0
  67. package/dist/cjs/src/core/SessionManager.d.ts.map +1 -0
  68. package/dist/cjs/src/core/SessionManager.js +197 -0
  69. package/dist/cjs/src/core/SessionManager.js.map +1 -0
  70. package/dist/cjs/src/core/Step.d.ts +96 -0
  71. package/dist/cjs/src/core/Step.d.ts.map +1 -0
  72. package/dist/cjs/src/core/Step.js +206 -0
  73. package/dist/cjs/src/core/Step.js.map +1 -0
  74. package/dist/cjs/src/core/ToolExecutor.d.ts +43 -0
  75. package/dist/cjs/src/core/ToolExecutor.d.ts.map +1 -0
  76. package/dist/cjs/{core → src/core}/ToolExecutor.js +19 -18
  77. package/dist/cjs/src/core/ToolExecutor.js.map +1 -0
  78. package/dist/{index.d.ts → cjs/src/index.d.ts} +7 -15
  79. package/dist/cjs/src/index.d.ts.map +1 -0
  80. package/dist/cjs/{index.js → src/index.js} +21 -19
  81. package/dist/cjs/src/index.js.map +1 -0
  82. package/dist/cjs/{providers → src/providers}/AnthropicProvider.d.ts +1 -1
  83. package/dist/cjs/src/providers/AnthropicProvider.d.ts.map +1 -0
  84. package/dist/cjs/{providers → src/providers}/AnthropicProvider.js +54 -2
  85. package/dist/cjs/src/providers/AnthropicProvider.js.map +1 -0
  86. package/dist/{providers → cjs/src/providers}/GeminiProvider.d.ts +1 -1
  87. package/dist/cjs/src/providers/GeminiProvider.d.ts.map +1 -0
  88. package/dist/cjs/{providers → src/providers}/GeminiProvider.js +65 -0
  89. package/dist/cjs/src/providers/GeminiProvider.js.map +1 -0
  90. package/dist/cjs/{providers → src/providers}/OpenAIProvider.d.ts +1 -1
  91. package/dist/cjs/src/providers/OpenAIProvider.d.ts.map +1 -0
  92. package/dist/cjs/{providers → src/providers}/OpenAIProvider.js +70 -1
  93. package/dist/cjs/src/providers/OpenAIProvider.js.map +1 -0
  94. package/dist/{providers → cjs/src/providers}/OpenRouterProvider.d.ts +1 -1
  95. package/dist/cjs/src/providers/OpenRouterProvider.d.ts.map +1 -0
  96. package/dist/cjs/{providers → src/providers}/OpenRouterProvider.js +76 -0
  97. package/dist/cjs/src/providers/OpenRouterProvider.js.map +1 -0
  98. package/dist/cjs/src/providers/index.d.ts.map +1 -0
  99. package/dist/cjs/src/providers/index.js.map +1 -0
  100. package/dist/cjs/{types → src/types}/agent.d.ts +52 -33
  101. package/dist/cjs/src/types/agent.d.ts.map +1 -0
  102. package/dist/cjs/src/types/agent.js.map +1 -0
  103. package/dist/cjs/{types → src/types}/ai.d.ts +7 -0
  104. package/dist/cjs/src/types/ai.d.ts.map +1 -0
  105. package/dist/cjs/src/types/ai.js.map +1 -0
  106. package/dist/{types → cjs/src/types}/history.d.ts +76 -18
  107. package/dist/cjs/src/types/history.d.ts.map +1 -0
  108. package/dist/cjs/src/types/history.js +33 -0
  109. package/dist/cjs/src/types/history.js.map +1 -0
  110. package/dist/cjs/src/types/index.d.ts +20 -0
  111. package/dist/cjs/src/types/index.d.ts.map +1 -0
  112. package/dist/cjs/src/types/index.js +30 -0
  113. package/dist/cjs/src/types/index.js.map +1 -0
  114. package/dist/{types → cjs/src/types}/persistence.d.ts +39 -23
  115. package/dist/cjs/src/types/persistence.d.ts.map +1 -0
  116. package/dist/cjs/src/types/persistence.js.map +1 -0
  117. package/dist/cjs/{types → src/types}/route.d.ts +85 -31
  118. package/dist/cjs/src/types/route.d.ts.map +1 -0
  119. package/dist/cjs/{types → src/types}/route.js.map +1 -1
  120. package/dist/cjs/src/types/routing.d.ts.map +1 -0
  121. package/dist/{types → cjs/src/types}/routing.js.map +1 -1
  122. package/dist/cjs/src/types/schema.d.ts.map +1 -0
  123. package/dist/{types → cjs/src/types}/schema.js.map +1 -1
  124. package/dist/cjs/src/types/session.d.ts +70 -0
  125. package/dist/cjs/src/types/session.d.ts.map +1 -0
  126. package/dist/cjs/src/types/session.js +6 -0
  127. package/dist/cjs/src/types/session.js.map +1 -0
  128. package/dist/cjs/src/types/template.d.ts +30 -0
  129. package/dist/cjs/src/types/template.d.ts.map +1 -0
  130. package/dist/cjs/src/types/template.js +3 -0
  131. package/dist/cjs/src/types/template.js.map +1 -0
  132. package/dist/cjs/{types → src/types}/tool.d.ts +6 -8
  133. package/dist/cjs/src/types/tool.d.ts.map +1 -0
  134. package/dist/cjs/{types → src/types}/tool.js.map +1 -1
  135. package/dist/cjs/src/utils/clone.d.ts +8 -0
  136. package/dist/cjs/src/utils/clone.d.ts.map +1 -0
  137. package/dist/cjs/src/utils/clone.js +36 -0
  138. package/dist/cjs/src/utils/clone.js.map +1 -0
  139. package/dist/{utils → cjs/src/utils}/event.d.ts +1 -1
  140. package/dist/cjs/src/utils/event.d.ts.map +1 -0
  141. package/dist/cjs/{utils → src/utils}/event.js +2 -2
  142. package/dist/cjs/src/utils/event.js.map +1 -0
  143. package/dist/cjs/src/utils/history.d.ts +31 -0
  144. package/dist/cjs/src/utils/history.d.ts.map +1 -0
  145. package/dist/cjs/src/utils/history.js +128 -0
  146. package/dist/cjs/src/utils/history.js.map +1 -0
  147. package/dist/cjs/src/utils/id.d.ts.map +1 -0
  148. package/dist/cjs/src/utils/id.js.map +1 -0
  149. package/dist/cjs/src/utils/index.d.ts +13 -0
  150. package/dist/cjs/src/utils/index.d.ts.map +1 -0
  151. package/dist/cjs/src/utils/index.js +49 -0
  152. package/dist/cjs/src/utils/index.js.map +1 -0
  153. package/dist/cjs/src/utils/logger.d.ts.map +1 -0
  154. package/dist/cjs/src/utils/logger.js.map +1 -0
  155. package/dist/cjs/src/utils/retry.d.ts.map +1 -0
  156. package/dist/cjs/src/utils/retry.js.map +1 -0
  157. package/dist/cjs/src/utils/session.d.ts +51 -0
  158. package/dist/cjs/src/utils/session.d.ts.map +1 -0
  159. package/dist/cjs/{types → src/utils}/session.js +34 -11
  160. package/dist/cjs/src/utils/session.js.map +1 -0
  161. package/dist/cjs/src/utils/template.d.ts +107 -0
  162. package/dist/cjs/src/utils/template.d.ts.map +1 -0
  163. package/dist/cjs/src/utils/template.js +283 -0
  164. package/dist/cjs/src/utils/template.js.map +1 -0
  165. package/dist/{cjs → src}/adapters/MemoryAdapter.d.ts +4 -4
  166. package/dist/src/adapters/MemoryAdapter.d.ts.map +1 -0
  167. package/dist/{adapters → src/adapters}/MemoryAdapter.js +41 -21
  168. package/dist/src/adapters/MemoryAdapter.js.map +1 -0
  169. package/dist/{cjs → src}/adapters/MongoAdapter.d.ts +3 -3
  170. package/dist/src/adapters/MongoAdapter.d.ts.map +1 -0
  171. package/dist/{adapters → src/adapters}/MongoAdapter.js +2 -1
  172. package/dist/src/adapters/MongoAdapter.js.map +1 -0
  173. package/dist/{adapters → src/adapters}/OpenSearchAdapter.d.ts +3 -3
  174. package/dist/src/adapters/OpenSearchAdapter.d.ts.map +1 -0
  175. package/dist/{adapters → src/adapters}/OpenSearchAdapter.js +10 -13
  176. package/dist/src/adapters/OpenSearchAdapter.js.map +1 -0
  177. package/dist/{adapters → src/adapters}/PostgreSQLAdapter.d.ts +3 -3
  178. package/dist/src/adapters/PostgreSQLAdapter.d.ts.map +1 -0
  179. package/dist/{adapters → src/adapters}/PostgreSQLAdapter.js +1 -1
  180. package/dist/src/adapters/PostgreSQLAdapter.js.map +1 -0
  181. package/dist/{adapters → src/adapters}/PrismaAdapter.d.ts +3 -3
  182. package/dist/src/adapters/PrismaAdapter.d.ts.map +1 -0
  183. package/dist/{adapters → src/adapters}/PrismaAdapter.js +35 -5
  184. package/dist/src/adapters/PrismaAdapter.js.map +1 -0
  185. package/dist/{adapters → src/adapters}/RedisAdapter.d.ts +3 -3
  186. package/dist/src/adapters/RedisAdapter.d.ts.map +1 -0
  187. package/dist/{adapters → src/adapters}/RedisAdapter.js +3 -2
  188. package/dist/src/adapters/RedisAdapter.js.map +1 -0
  189. package/dist/{cjs → src}/adapters/SQLiteAdapter.d.ts +3 -3
  190. package/dist/src/adapters/SQLiteAdapter.d.ts.map +1 -0
  191. package/dist/{adapters → src/adapters}/SQLiteAdapter.js +2 -1
  192. package/dist/src/adapters/SQLiteAdapter.js.map +1 -0
  193. package/dist/src/adapters/index.js.map +1 -0
  194. package/dist/src/constants/index.js.map +1 -0
  195. package/dist/{cjs → src}/core/Agent.d.ts +65 -67
  196. package/dist/src/core/Agent.d.ts.map +1 -0
  197. package/dist/src/core/Agent.js +1429 -0
  198. package/dist/src/core/Agent.js.map +1 -0
  199. package/dist/src/core/Events.d.ts +26 -0
  200. package/dist/src/core/Events.d.ts.map +1 -0
  201. package/dist/src/core/Events.js +137 -0
  202. package/dist/src/core/Events.js.map +1 -0
  203. package/dist/{cjs → src}/core/PersistenceManager.d.ts +21 -19
  204. package/dist/src/core/PersistenceManager.d.ts.map +1 -0
  205. package/dist/{core → src/core}/PersistenceManager.js +47 -17
  206. package/dist/src/core/PersistenceManager.js.map +1 -0
  207. package/dist/src/core/PromptComposer.d.ts +27 -0
  208. package/dist/src/core/PromptComposer.d.ts.map +1 -0
  209. package/dist/src/core/PromptComposer.js +153 -0
  210. package/dist/src/core/PromptComposer.js.map +1 -0
  211. package/dist/src/core/ResponseEngine.d.ts +31 -0
  212. package/dist/src/core/ResponseEngine.d.ts.map +1 -0
  213. package/dist/src/core/ResponseEngine.js +80 -0
  214. package/dist/src/core/ResponseEngine.js.map +1 -0
  215. package/dist/src/core/ResponsePipeline.d.ts +143 -0
  216. package/dist/src/core/ResponsePipeline.d.ts.map +1 -0
  217. package/dist/src/core/ResponsePipeline.js +442 -0
  218. package/dist/src/core/ResponsePipeline.js.map +1 -0
  219. package/dist/src/core/Route.d.ts +126 -0
  220. package/dist/src/core/Route.d.ts.map +1 -0
  221. package/dist/{core → src/core}/Route.js +116 -20
  222. package/dist/src/core/Route.js.map +1 -0
  223. package/dist/{cjs → src}/core/RoutingEngine.d.ts +33 -38
  224. package/dist/src/core/RoutingEngine.d.ts.map +1 -0
  225. package/dist/{core → src/core}/RoutingEngine.js +98 -104
  226. package/dist/src/core/RoutingEngine.js.map +1 -0
  227. package/dist/src/core/SessionManager.d.ts +76 -0
  228. package/dist/src/core/SessionManager.d.ts.map +1 -0
  229. package/dist/src/core/SessionManager.js +193 -0
  230. package/dist/src/core/SessionManager.js.map +1 -0
  231. package/dist/src/core/Step.d.ts +96 -0
  232. package/dist/src/core/Step.d.ts.map +1 -0
  233. package/dist/src/core/Step.js +202 -0
  234. package/dist/src/core/Step.js.map +1 -0
  235. package/dist/src/core/ToolExecutor.d.ts +43 -0
  236. package/dist/src/core/ToolExecutor.d.ts.map +1 -0
  237. package/dist/src/core/ToolExecutor.js +70 -0
  238. package/dist/src/core/ToolExecutor.js.map +1 -0
  239. package/dist/{cjs → src}/index.d.ts +7 -15
  240. package/dist/src/index.d.ts.map +1 -0
  241. package/dist/{index.js → src/index.js} +6 -7
  242. package/dist/src/index.js.map +1 -0
  243. package/dist/{providers → src/providers}/AnthropicProvider.d.ts +1 -1
  244. package/dist/src/providers/AnthropicProvider.d.ts.map +1 -0
  245. package/dist/{providers → src/providers}/AnthropicProvider.js +54 -2
  246. package/dist/src/providers/AnthropicProvider.js.map +1 -0
  247. package/dist/{cjs → src}/providers/GeminiProvider.d.ts +1 -1
  248. package/dist/{cjs → src}/providers/GeminiProvider.d.ts.map +1 -1
  249. package/dist/{providers → src/providers}/GeminiProvider.js +65 -0
  250. package/dist/src/providers/GeminiProvider.js.map +1 -0
  251. package/dist/{providers → src/providers}/OpenAIProvider.d.ts +1 -1
  252. package/dist/{cjs → src}/providers/OpenAIProvider.d.ts.map +1 -1
  253. package/dist/{providers → src/providers}/OpenAIProvider.js +70 -1
  254. package/dist/src/providers/OpenAIProvider.js.map +1 -0
  255. package/dist/{cjs → src}/providers/OpenRouterProvider.d.ts +1 -1
  256. package/dist/{cjs → src}/providers/OpenRouterProvider.d.ts.map +1 -1
  257. package/dist/{providers → src/providers}/OpenRouterProvider.js +76 -0
  258. package/dist/src/providers/OpenRouterProvider.js.map +1 -0
  259. package/dist/src/providers/index.js.map +1 -0
  260. package/dist/{types → src/types}/agent.d.ts +52 -33
  261. package/dist/src/types/agent.d.ts.map +1 -0
  262. package/dist/src/types/agent.js.map +1 -0
  263. package/dist/{types → src/types}/ai.d.ts +7 -0
  264. package/dist/src/types/ai.d.ts.map +1 -0
  265. package/dist/{cjs → src}/types/ai.js.map +1 -1
  266. package/dist/{cjs → src}/types/history.d.ts +76 -18
  267. package/dist/src/types/history.d.ts.map +1 -0
  268. package/dist/src/types/history.js +30 -0
  269. package/dist/src/types/history.js.map +1 -0
  270. package/dist/src/types/index.d.ts +20 -0
  271. package/dist/src/types/index.d.ts.map +1 -0
  272. package/dist/src/types/index.js +10 -0
  273. package/dist/src/types/index.js.map +1 -0
  274. package/dist/{cjs → src}/types/persistence.d.ts +39 -23
  275. package/dist/src/types/persistence.d.ts.map +1 -0
  276. package/dist/src/types/persistence.js.map +1 -0
  277. package/dist/{types → src/types}/route.d.ts +85 -31
  278. package/dist/src/types/route.d.ts.map +1 -0
  279. package/dist/{types → src/types}/route.js.map +1 -1
  280. package/dist/src/types/session.d.ts +70 -0
  281. package/dist/src/types/session.d.ts.map +1 -0
  282. package/dist/src/types/session.js +5 -0
  283. package/dist/src/types/session.js.map +1 -0
  284. package/dist/src/types/template.d.ts +30 -0
  285. package/dist/src/types/template.d.ts.map +1 -0
  286. package/dist/src/types/template.js +2 -0
  287. package/dist/src/types/template.js.map +1 -0
  288. package/dist/{types → src/types}/tool.d.ts +6 -8
  289. package/dist/{cjs → src}/types/tool.d.ts.map +1 -1
  290. package/dist/{types → src/types}/tool.js.map +1 -1
  291. package/dist/src/utils/clone.d.ts +8 -0
  292. package/dist/src/utils/clone.d.ts.map +1 -0
  293. package/dist/src/utils/clone.js +33 -0
  294. package/dist/src/utils/clone.js.map +1 -0
  295. package/dist/{cjs → src}/utils/event.d.ts +1 -1
  296. package/dist/{cjs → src}/utils/event.d.ts.map +1 -1
  297. package/dist/{utils → src/utils}/event.js +1 -1
  298. package/dist/src/utils/event.js.map +1 -0
  299. package/dist/src/utils/history.d.ts +31 -0
  300. package/dist/src/utils/history.d.ts.map +1 -0
  301. package/dist/src/utils/history.js +121 -0
  302. package/dist/src/utils/history.js.map +1 -0
  303. package/dist/src/utils/id.js.map +1 -0
  304. package/dist/src/utils/index.d.ts +13 -0
  305. package/dist/src/utils/index.d.ts.map +1 -0
  306. package/dist/src/utils/index.js +19 -0
  307. package/dist/src/utils/index.js.map +1 -0
  308. package/dist/src/utils/logger.js.map +1 -0
  309. package/dist/src/utils/retry.js.map +1 -0
  310. package/dist/src/utils/session.d.ts +51 -0
  311. package/dist/src/utils/session.d.ts.map +1 -0
  312. package/dist/{types → src/utils}/session.js +32 -11
  313. package/dist/src/utils/session.js.map +1 -0
  314. package/dist/src/utils/template.d.ts +107 -0
  315. package/dist/src/utils/template.d.ts.map +1 -0
  316. package/dist/src/utils/template.js +276 -0
  317. package/dist/src/utils/template.js.map +1 -0
  318. package/docs/README.md +174 -68
  319. package/docs/{API_REFERENCE.md → api/README.md} +890 -251
  320. package/docs/api/overview.md +798 -0
  321. package/docs/core/agent/README.md +642 -0
  322. package/docs/{CONTEXT_MANAGEMENT.md → core/agent/context-management.md} +143 -94
  323. package/docs/{ARCHITECTURE.md → core/agent/session-management.md} +74 -59
  324. package/docs/core/ai-integration/prompt-composition.md +196 -0
  325. package/docs/core/ai-integration/providers.md +515 -0
  326. package/docs/core/ai-integration/response-processing.md +165 -0
  327. package/docs/core/conversation-flows/data-collection.md +545 -0
  328. package/docs/core/conversation-flows/route-dsl.md +479 -0
  329. package/docs/core/conversation-flows/routes.md +61 -0
  330. package/docs/core/conversation-flows/step-transitions.md +595 -0
  331. package/docs/core/conversation-flows/steps.md +130 -0
  332. package/docs/{ADAPTERS.md → core/persistence/adapters.md} +1 -1
  333. package/docs/core/persistence/session-storage.md +644 -0
  334. package/docs/core/routing/intelligent-routing.md +339 -0
  335. package/docs/core/tools/tool-definition.md +346 -0
  336. package/docs/core/tools/tool-execution.md +815 -0
  337. package/docs/core/tools/tool-scoping.md +628 -0
  338. package/docs/guides/getting-started/README.md +384 -0
  339. package/examples/{company-qna-agent.ts → advanced-patterns/knowledge-based-agent.ts} +104 -69
  340. package/examples/{persistent-onboarding.ts → advanced-patterns/persistent-onboarding.ts} +181 -103
  341. package/examples/{rules-prohibitions.ts → advanced-patterns/route-lifecycle-hooks.ts} +102 -82
  342. package/examples/{streaming-agent.ts → advanced-patterns/streaming-responses.ts} +90 -69
  343. package/examples/ai-providers/anthropic-integration.ts +377 -0
  344. package/examples/{openai-agent.ts → ai-providers/openai-integration.ts} +37 -43
  345. package/examples/{route-transitions.ts → conversation-flows/completion-transitions.ts} +112 -105
  346. package/examples/{declarative-agent.ts → core-concepts/basic-agent.ts} +175 -131
  347. package/examples/core-concepts/schema-driven-extraction.ts +301 -0
  348. package/examples/core-concepts/session-management.ts +394 -0
  349. package/examples/integrations/database-integration.ts +615 -0
  350. package/examples/{healthcare-agent.ts → integrations/healthcare-integration.ts} +204 -111
  351. package/examples/{opensearch-persistence.ts → integrations/search-integration.ts} +159 -128
  352. package/examples/integrations/server-session-management.ts +299 -0
  353. package/examples/persistence/custom-adapter.ts +529 -0
  354. package/examples/{prisma-persistence.ts → persistence/database-persistence.ts} +168 -241
  355. package/examples/persistence/memory-sessions.ts +506 -0
  356. package/examples/{prisma-schema.example.prisma → persistence/prisma-schema.example.prisma} +1 -1
  357. package/examples/{redis-persistence.ts → persistence/redis-persistence.ts} +152 -173
  358. package/examples/tools/basic-tools.ts +550 -0
  359. package/examples/{extracted-data-modification.ts → tools/data-enrichment-tools.ts} +82 -79
  360. package/package.json +14 -10
  361. package/src/adapters/MemoryAdapter.ts +74 -46
  362. package/src/adapters/MongoAdapter.ts +33 -24
  363. package/src/adapters/OpenSearchAdapter.ts +41 -37
  364. package/src/adapters/PostgreSQLAdapter.ts +35 -24
  365. package/src/adapters/PrismaAdapter.ts +69 -27
  366. package/src/adapters/RedisAdapter.ts +38 -26
  367. package/src/adapters/SQLiteAdapter.ts +32 -22
  368. package/src/core/Agent.ts +1093 -478
  369. package/src/core/Events.ts +100 -112
  370. package/src/core/PersistenceManager.ts +77 -47
  371. package/src/core/PromptComposer.ts +158 -85
  372. package/src/core/ResponseEngine.ts +118 -38
  373. package/src/core/ResponsePipeline.ts +715 -0
  374. package/src/core/Route.ts +168 -51
  375. package/src/core/RoutingEngine.ts +178 -209
  376. package/src/core/SessionManager.ts +241 -0
  377. package/src/core/Step.ts +149 -67
  378. package/src/core/ToolExecutor.ts +37 -42
  379. package/src/index.ts +31 -37
  380. package/src/providers/AnthropicProvider.ts +71 -5
  381. package/src/providers/GeminiProvider.ts +83 -2
  382. package/src/providers/OpenAIProvider.ts +95 -3
  383. package/src/providers/OpenRouterProvider.ts +102 -2
  384. package/src/types/agent.ts +48 -36
  385. package/src/types/ai.ts +7 -0
  386. package/src/types/history.ts +91 -18
  387. package/src/types/index.ts +43 -7
  388. package/src/types/persistence.ts +46 -28
  389. package/src/types/route.ts +104 -45
  390. package/src/types/session.ts +19 -213
  391. package/src/types/template.ts +36 -0
  392. package/src/types/tool.ts +9 -11
  393. package/src/utils/clone.ts +36 -0
  394. package/src/utils/event.ts +1 -1
  395. package/src/utils/history.ts +143 -0
  396. package/src/utils/index.ts +53 -0
  397. package/src/utils/session.ts +229 -0
  398. package/src/utils/template.ts +335 -0
  399. package/dist/adapters/MemoryAdapter.d.ts.map +0 -1
  400. package/dist/adapters/MemoryAdapter.js.map +0 -1
  401. package/dist/adapters/MongoAdapter.d.ts.map +0 -1
  402. package/dist/adapters/MongoAdapter.js.map +0 -1
  403. package/dist/adapters/OpenSearchAdapter.d.ts.map +0 -1
  404. package/dist/adapters/OpenSearchAdapter.js.map +0 -1
  405. package/dist/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  406. package/dist/adapters/PostgreSQLAdapter.js.map +0 -1
  407. package/dist/adapters/PrismaAdapter.d.ts.map +0 -1
  408. package/dist/adapters/PrismaAdapter.js.map +0 -1
  409. package/dist/adapters/RedisAdapter.d.ts.map +0 -1
  410. package/dist/adapters/RedisAdapter.js.map +0 -1
  411. package/dist/adapters/SQLiteAdapter.d.ts.map +0 -1
  412. package/dist/adapters/SQLiteAdapter.js.map +0 -1
  413. package/dist/adapters/index.d.ts.map +0 -1
  414. package/dist/adapters/index.js.map +0 -1
  415. package/dist/cjs/adapters/MemoryAdapter.d.ts.map +0 -1
  416. package/dist/cjs/adapters/MemoryAdapter.js.map +0 -1
  417. package/dist/cjs/adapters/MongoAdapter.d.ts.map +0 -1
  418. package/dist/cjs/adapters/MongoAdapter.js.map +0 -1
  419. package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +0 -1
  420. package/dist/cjs/adapters/OpenSearchAdapter.js.map +0 -1
  421. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +0 -1
  422. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +0 -1
  423. package/dist/cjs/adapters/PrismaAdapter.d.ts.map +0 -1
  424. package/dist/cjs/adapters/PrismaAdapter.js.map +0 -1
  425. package/dist/cjs/adapters/RedisAdapter.d.ts.map +0 -1
  426. package/dist/cjs/adapters/RedisAdapter.js.map +0 -1
  427. package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +0 -1
  428. package/dist/cjs/adapters/SQLiteAdapter.js.map +0 -1
  429. package/dist/cjs/adapters/index.js.map +0 -1
  430. package/dist/cjs/constants/index.js.map +0 -1
  431. package/dist/cjs/core/Agent.d.ts.map +0 -1
  432. package/dist/cjs/core/Agent.js +0 -966
  433. package/dist/cjs/core/Agent.js.map +0 -1
  434. package/dist/cjs/core/DomainRegistry.d.ts +0 -36
  435. package/dist/cjs/core/DomainRegistry.d.ts.map +0 -1
  436. package/dist/cjs/core/DomainRegistry.js +0 -72
  437. package/dist/cjs/core/DomainRegistry.js.map +0 -1
  438. package/dist/cjs/core/Events.d.ts +0 -41
  439. package/dist/cjs/core/Events.d.ts.map +0 -1
  440. package/dist/cjs/core/Events.js +0 -99
  441. package/dist/cjs/core/Events.js.map +0 -1
  442. package/dist/cjs/core/PersistenceManager.d.ts.map +0 -1
  443. package/dist/cjs/core/PersistenceManager.js.map +0 -1
  444. package/dist/cjs/core/PromptComposer.d.ts +0 -24
  445. package/dist/cjs/core/PromptComposer.d.ts.map +0 -1
  446. package/dist/cjs/core/PromptComposer.js +0 -127
  447. package/dist/cjs/core/PromptComposer.js.map +0 -1
  448. package/dist/cjs/core/ResponseEngine.d.ts +0 -14
  449. package/dist/cjs/core/ResponseEngine.d.ts.map +0 -1
  450. package/dist/cjs/core/ResponseEngine.js +0 -56
  451. package/dist/cjs/core/ResponseEngine.js.map +0 -1
  452. package/dist/cjs/core/Route.d.ts +0 -90
  453. package/dist/cjs/core/Route.d.ts.map +0 -1
  454. package/dist/cjs/core/Route.js.map +0 -1
  455. package/dist/cjs/core/RoutingEngine.d.ts.map +0 -1
  456. package/dist/cjs/core/RoutingEngine.js.map +0 -1
  457. package/dist/cjs/core/Step.d.ts +0 -72
  458. package/dist/cjs/core/Step.d.ts.map +0 -1
  459. package/dist/cjs/core/Step.js +0 -150
  460. package/dist/cjs/core/Step.js.map +0 -1
  461. package/dist/cjs/core/Tool.d.ts +0 -39
  462. package/dist/cjs/core/Tool.d.ts.map +0 -1
  463. package/dist/cjs/core/Tool.js +0 -34
  464. package/dist/cjs/core/Tool.js.map +0 -1
  465. package/dist/cjs/core/ToolExecutor.d.ts +0 -29
  466. package/dist/cjs/core/ToolExecutor.d.ts.map +0 -1
  467. package/dist/cjs/core/ToolExecutor.js.map +0 -1
  468. package/dist/cjs/core/Transition.d.ts +0 -32
  469. package/dist/cjs/core/Transition.d.ts.map +0 -1
  470. package/dist/cjs/core/Transition.js +0 -89
  471. package/dist/cjs/core/Transition.js.map +0 -1
  472. package/dist/cjs/index.d.ts.map +0 -1
  473. package/dist/cjs/index.js.map +0 -1
  474. package/dist/cjs/providers/AnthropicProvider.d.ts.map +0 -1
  475. package/dist/cjs/providers/AnthropicProvider.js.map +0 -1
  476. package/dist/cjs/providers/GeminiProvider.js.map +0 -1
  477. package/dist/cjs/providers/OpenAIProvider.js.map +0 -1
  478. package/dist/cjs/providers/OpenRouterProvider.js.map +0 -1
  479. package/dist/cjs/providers/index.js.map +0 -1
  480. package/dist/cjs/types/agent.d.ts.map +0 -1
  481. package/dist/cjs/types/agent.js.map +0 -1
  482. package/dist/cjs/types/ai.d.ts.map +0 -1
  483. package/dist/cjs/types/history.d.ts.map +0 -1
  484. package/dist/cjs/types/history.js +0 -37
  485. package/dist/cjs/types/history.js.map +0 -1
  486. package/dist/cjs/types/index.d.ts +0 -12
  487. package/dist/cjs/types/index.d.ts.map +0 -1
  488. package/dist/cjs/types/index.js +0 -12
  489. package/dist/cjs/types/index.js.map +0 -1
  490. package/dist/cjs/types/persistence.d.ts.map +0 -1
  491. package/dist/cjs/types/persistence.js.map +0 -1
  492. package/dist/cjs/types/route.d.ts.map +0 -1
  493. package/dist/cjs/types/session.d.ts +0 -104
  494. package/dist/cjs/types/session.d.ts.map +0 -1
  495. package/dist/cjs/types/session.js.map +0 -1
  496. package/dist/cjs/utils/event.js.map +0 -1
  497. package/dist/cjs/utils/id.js.map +0 -1
  498. package/dist/cjs/utils/logger.js.map +0 -1
  499. package/dist/cjs/utils/retry.js.map +0 -1
  500. package/dist/constants/index.d.ts.map +0 -1
  501. package/dist/constants/index.js.map +0 -1
  502. package/dist/core/Agent.d.ts.map +0 -1
  503. package/dist/core/Agent.js +0 -962
  504. package/dist/core/Agent.js.map +0 -1
  505. package/dist/core/DomainRegistry.d.ts +0 -36
  506. package/dist/core/DomainRegistry.d.ts.map +0 -1
  507. package/dist/core/DomainRegistry.js +0 -68
  508. package/dist/core/DomainRegistry.js.map +0 -1
  509. package/dist/core/Events.d.ts +0 -41
  510. package/dist/core/Events.d.ts.map +0 -1
  511. package/dist/core/Events.js +0 -94
  512. package/dist/core/Events.js.map +0 -1
  513. package/dist/core/PersistenceManager.d.ts.map +0 -1
  514. package/dist/core/PersistenceManager.js.map +0 -1
  515. package/dist/core/PromptComposer.d.ts +0 -24
  516. package/dist/core/PromptComposer.d.ts.map +0 -1
  517. package/dist/core/PromptComposer.js +0 -123
  518. package/dist/core/PromptComposer.js.map +0 -1
  519. package/dist/core/ResponseEngine.d.ts +0 -14
  520. package/dist/core/ResponseEngine.d.ts.map +0 -1
  521. package/dist/core/ResponseEngine.js +0 -52
  522. package/dist/core/ResponseEngine.js.map +0 -1
  523. package/dist/core/Route.d.ts +0 -90
  524. package/dist/core/Route.d.ts.map +0 -1
  525. package/dist/core/Route.js.map +0 -1
  526. package/dist/core/RoutingEngine.d.ts.map +0 -1
  527. package/dist/core/RoutingEngine.js.map +0 -1
  528. package/dist/core/Step.d.ts +0 -72
  529. package/dist/core/Step.d.ts.map +0 -1
  530. package/dist/core/Step.js +0 -146
  531. package/dist/core/Step.js.map +0 -1
  532. package/dist/core/Tool.d.ts +0 -39
  533. package/dist/core/Tool.d.ts.map +0 -1
  534. package/dist/core/Tool.js +0 -31
  535. package/dist/core/Tool.js.map +0 -1
  536. package/dist/core/ToolExecutor.d.ts +0 -29
  537. package/dist/core/ToolExecutor.d.ts.map +0 -1
  538. package/dist/core/ToolExecutor.js +0 -69
  539. package/dist/core/ToolExecutor.js.map +0 -1
  540. package/dist/core/Transition.d.ts +0 -32
  541. package/dist/core/Transition.d.ts.map +0 -1
  542. package/dist/core/Transition.js +0 -85
  543. package/dist/core/Transition.js.map +0 -1
  544. package/dist/index.d.ts.map +0 -1
  545. package/dist/index.js.map +0 -1
  546. package/dist/providers/AnthropicProvider.d.ts.map +0 -1
  547. package/dist/providers/AnthropicProvider.js.map +0 -1
  548. package/dist/providers/GeminiProvider.d.ts.map +0 -1
  549. package/dist/providers/GeminiProvider.js.map +0 -1
  550. package/dist/providers/OpenAIProvider.d.ts.map +0 -1
  551. package/dist/providers/OpenAIProvider.js.map +0 -1
  552. package/dist/providers/OpenRouterProvider.d.ts.map +0 -1
  553. package/dist/providers/OpenRouterProvider.js.map +0 -1
  554. package/dist/providers/index.d.ts.map +0 -1
  555. package/dist/providers/index.js.map +0 -1
  556. package/dist/types/agent.d.ts.map +0 -1
  557. package/dist/types/agent.js.map +0 -1
  558. package/dist/types/ai.d.ts.map +0 -1
  559. package/dist/types/ai.js.map +0 -1
  560. package/dist/types/history.d.ts.map +0 -1
  561. package/dist/types/history.js +0 -34
  562. package/dist/types/history.js.map +0 -1
  563. package/dist/types/index.d.ts +0 -12
  564. package/dist/types/index.d.ts.map +0 -1
  565. package/dist/types/index.js +0 -6
  566. package/dist/types/index.js.map +0 -1
  567. package/dist/types/persistence.d.ts.map +0 -1
  568. package/dist/types/persistence.js.map +0 -1
  569. package/dist/types/route.d.ts.map +0 -1
  570. package/dist/types/routing.d.ts.map +0 -1
  571. package/dist/types/schema.d.ts.map +0 -1
  572. package/dist/types/session.d.ts +0 -104
  573. package/dist/types/session.d.ts.map +0 -1
  574. package/dist/types/session.js.map +0 -1
  575. package/dist/types/tool.d.ts.map +0 -1
  576. package/dist/utils/event.d.ts.map +0 -1
  577. package/dist/utils/event.js.map +0 -1
  578. package/dist/utils/id.d.ts.map +0 -1
  579. package/dist/utils/id.js.map +0 -1
  580. package/dist/utils/logger.d.ts.map +0 -1
  581. package/dist/utils/logger.js.map +0 -1
  582. package/dist/utils/retry.d.ts.map +0 -1
  583. package/dist/utils/retry.js.map +0 -1
  584. package/docs/AGENT.md +0 -535
  585. package/docs/DOCS.md +0 -263
  586. package/docs/DOMAINS.md +0 -735
  587. package/docs/EXAMPLES.md +0 -467
  588. package/docs/GETTING_STARTED.md +0 -424
  589. package/docs/PERSISTENCE.md +0 -815
  590. package/docs/PROVIDERS.md +0 -612
  591. package/docs/ROUTES.md +0 -1085
  592. package/docs/STEPS.md +0 -883
  593. package/examples/business-onboarding.ts +0 -791
  594. package/examples/custom-database-persistence.ts +0 -574
  595. package/examples/domain-scoping.ts +0 -366
  596. package/examples/travel-agent.ts +0 -584
  597. package/src/core/DomainRegistry.ts +0 -80
  598. package/src/core/Tool.ts +0 -112
  599. package/src/core/Transition.ts +0 -115
  600. /package/dist/{adapters → cjs/src/adapters}/index.d.ts +0 -0
  601. /package/dist/cjs/{adapters → src/adapters}/index.js +0 -0
  602. /package/dist/cjs/{constants → src/constants}/index.d.ts +0 -0
  603. /package/dist/cjs/{constants → src/constants}/index.js +0 -0
  604. /package/dist/cjs/{providers → src/providers}/index.d.ts +0 -0
  605. /package/dist/cjs/{providers → src/providers}/index.js +0 -0
  606. /package/dist/cjs/{types → src/types}/agent.js +0 -0
  607. /package/dist/cjs/{types → src/types}/ai.js +0 -0
  608. /package/dist/cjs/{types → src/types}/persistence.js +0 -0
  609. /package/dist/cjs/{types → src/types}/route.js +0 -0
  610. /package/dist/cjs/{types → src/types}/routing.d.ts +0 -0
  611. /package/dist/cjs/{types → src/types}/routing.js +0 -0
  612. /package/dist/cjs/{types → src/types}/schema.d.ts +0 -0
  613. /package/dist/cjs/{types → src/types}/schema.js +0 -0
  614. /package/dist/cjs/{types → src/types}/tool.js +0 -0
  615. /package/dist/cjs/{utils → src/utils}/id.d.ts +0 -0
  616. /package/dist/cjs/{utils → src/utils}/id.js +0 -0
  617. /package/dist/cjs/{utils → src/utils}/logger.d.ts +0 -0
  618. /package/dist/cjs/{utils → src/utils}/logger.js +0 -0
  619. /package/dist/cjs/{utils → src/utils}/retry.d.ts +0 -0
  620. /package/dist/cjs/{utils → src/utils}/retry.js +0 -0
  621. /package/dist/{cjs → src}/adapters/index.d.ts +0 -0
  622. /package/dist/{cjs → src}/adapters/index.d.ts.map +0 -0
  623. /package/dist/{adapters → src/adapters}/index.js +0 -0
  624. /package/dist/{constants → src/constants}/index.d.ts +0 -0
  625. /package/dist/{cjs → src}/constants/index.d.ts.map +0 -0
  626. /package/dist/{constants → src/constants}/index.js +0 -0
  627. /package/dist/{providers → src/providers}/index.d.ts +0 -0
  628. /package/dist/{cjs → src}/providers/index.d.ts.map +0 -0
  629. /package/dist/{providers → src/providers}/index.js +0 -0
  630. /package/dist/{types → src/types}/agent.js +0 -0
  631. /package/dist/{types → src/types}/ai.js +0 -0
  632. /package/dist/{types → src/types}/persistence.js +0 -0
  633. /package/dist/{types → src/types}/route.js +0 -0
  634. /package/dist/{types → src/types}/routing.d.ts +0 -0
  635. /package/dist/{cjs → src}/types/routing.d.ts.map +0 -0
  636. /package/dist/{types → src/types}/routing.js +0 -0
  637. /package/dist/{cjs → src}/types/routing.js.map +0 -0
  638. /package/dist/{types → src/types}/schema.d.ts +0 -0
  639. /package/dist/{cjs → src}/types/schema.d.ts.map +0 -0
  640. /package/dist/{types → src/types}/schema.js +0 -0
  641. /package/dist/{cjs → src}/types/schema.js.map +0 -0
  642. /package/dist/{types → src/types}/tool.js +0 -0
  643. /package/dist/{utils → src/utils}/id.d.ts +0 -0
  644. /package/dist/{cjs → src}/utils/id.d.ts.map +0 -0
  645. /package/dist/{utils → src/utils}/id.js +0 -0
  646. /package/dist/{utils → src/utils}/logger.d.ts +0 -0
  647. /package/dist/{cjs → src}/utils/logger.d.ts.map +0 -0
  648. /package/dist/{utils → src/utils}/logger.js +0 -0
  649. /package/dist/{utils → src/utils}/retry.d.ts +0 -0
  650. /package/dist/{cjs → src}/utils/retry.d.ts.map +0 -0
  651. /package/dist/{utils → src/utils}/retry.js +0 -0
  652. /package/docs/{PUBLISHING.md → guides/advanced-patterns/publishing.md} +0 -0
package/src/core/Agent.ts CHANGED
@@ -2,52 +2,64 @@
2
2
  * Core Agent implementation
3
3
  */
4
4
 
5
- import type { AgentOptions, Term, Guideline, Capability } from "../types/agent";
6
- import type { Event, StepRef } from "../types/index";
7
- import type { RouteOptions } from "../types/route";
8
-
9
- import type { SessionState } from "../types/session";
10
- import type { AgentStructuredResponse } from "../types/ai";
5
+ import type {
6
+ AgentOptions,
7
+ Term,
8
+ Guideline,
9
+ Tool,
10
+ Event,
11
+ RouteOptions,
12
+ SessionState,
13
+ AgentStructuredResponse,
14
+ Template,
15
+ StepRef,
16
+ History,
17
+ AgentResponseStreamChunk,
18
+ AgentResponse,
19
+ StructuredSchema,
20
+ } from "../types";
21
+ import { EventKind, MessageRole } from "../types/history";
11
22
  import {
12
- createSession,
13
23
  enterRoute,
14
24
  enterStep,
15
25
  mergeCollected,
16
- } from "../types/session";
17
- import { PromptComposer } from "./PromptComposer";
18
- import { logger, LoggerLevel } from "../utils/logger";
26
+ logger,
27
+ LoggerLevel,
28
+ render,
29
+ getLastMessageFromHistory,
30
+ normalizeHistory,
31
+ cloneDeep,
32
+ } from "../utils";
19
33
 
20
34
  import { Route } from "./Route";
21
35
  import { Step } from "./Step";
22
- import { DomainRegistry } from "./DomainRegistry";
23
36
  import { PersistenceManager } from "./PersistenceManager";
37
+ import { SessionManager } from "./SessionManager";
24
38
  import { RoutingEngine } from "./RoutingEngine";
25
39
  import { ResponseEngine } from "./ResponseEngine";
26
40
  import { ToolExecutor } from "./ToolExecutor";
27
- import { getLastMessageFromHistory } from "../utils/event";
41
+ import { ResponsePipeline } from "./ResponsePipeline";
28
42
  import { END_ROUTE_ID } from "../constants";
29
- import { ToolRef } from "../types";
30
43
 
31
44
  /**
32
45
  * Main Agent class with generic context support
33
46
  */
34
47
  export class Agent<TContext = unknown> {
35
- private terms: Term[] = [];
36
- private guidelines: Guideline[] = [];
37
- private capabilities: Capability[] = [];
48
+ private terms: Term<TContext>[] = [];
49
+ private guidelines: Guideline<TContext>[] = [];
50
+ private tools: Tool<TContext, unknown[], unknown, unknown>[] = [];
38
51
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
52
  private routes: Route<TContext, any>[] = [];
40
- private domainRegistry = new DomainRegistry();
41
53
  private context: TContext | undefined;
42
54
  private persistenceManager: PersistenceManager | undefined;
43
55
  private routingEngine: RoutingEngine<TContext>;
44
56
  private responseEngine: ResponseEngine<TContext>;
57
+ private responsePipeline: ResponsePipeline<TContext>;
45
58
  private currentSession?: SessionState;
59
+ private knowledgeBase: Record<string, unknown> = {};
46
60
 
47
- /**
48
- * Dynamic domain property - populated via addDomain
49
- */
50
- public readonly domain: Record<string, Record<string, unknown>> = {};
61
+ /** Public session manager for easy session management */
62
+ public session: SessionManager<unknown>;
51
63
 
52
64
  constructor(private readonly options: AgentOptions<TContext>) {
53
65
  // Set log level based on debug option
@@ -75,6 +87,14 @@ export class Agent<TContext = unknown> {
75
87
  switchThreshold: 70,
76
88
  });
77
89
  this.responseEngine = new ResponseEngine<TContext>();
90
+ this.responsePipeline = new ResponsePipeline<TContext>(
91
+ options,
92
+ this.routes,
93
+ this.tools,
94
+ this.routingEngine,
95
+ this.updateContext.bind(this),
96
+ this.updateData.bind(this)
97
+ );
78
98
 
79
99
  // Initialize persistence if configured
80
100
  if (options.persistence) {
@@ -91,9 +111,11 @@ export class Agent<TContext = unknown> {
91
111
  }
92
112
  }
93
113
 
94
- // Initialize from options
114
+ // Initialize from options - use create methods for consistency
95
115
  if (options.terms) {
96
- this.terms = [...options.terms];
116
+ options.terms.forEach((term) => {
117
+ this.createTerm(term);
118
+ });
97
119
  }
98
120
 
99
121
  if (options.guidelines) {
@@ -102,9 +124,9 @@ export class Agent<TContext = unknown> {
102
124
  });
103
125
  }
104
126
 
105
- if (options.capabilities) {
106
- options.capabilities.forEach((capability) => {
107
- this.createCapability(capability);
127
+ if (options.tools) {
128
+ options.tools.forEach((tool) => {
129
+ this.createTool(tool);
108
130
  });
109
131
  }
110
132
 
@@ -113,6 +135,22 @@ export class Agent<TContext = unknown> {
113
135
  this.createRoute<unknown>(routeOptions);
114
136
  });
115
137
  }
138
+
139
+ // Initialize knowledge base
140
+ if (options.knowledgeBase) {
141
+ this.knowledgeBase = { ...options.knowledgeBase };
142
+ }
143
+
144
+ // Initialize session manager
145
+ this.session = new SessionManager(this.persistenceManager);
146
+
147
+ // Store sessionId for later use in getOrCreate calls
148
+ if (options.sessionId) {
149
+ // The session will be loaded on first getOrCreate call
150
+ this.session.getOrCreate(options.sessionId).catch((err) => {
151
+ logger.error("Failed to start session", err);
152
+ });
153
+ }
116
154
  }
117
155
 
118
156
  /**
@@ -136,6 +174,13 @@ export class Agent<TContext = unknown> {
136
174
  return this.options.goal;
137
175
  }
138
176
 
177
+ /**
178
+ * Get agent identity
179
+ */
180
+ get identity(): Template<TContext> | undefined {
181
+ return this.options.identity;
182
+ }
183
+
139
184
  /**
140
185
  * Create a new route (journey)
141
186
  * @template TData - Type of data collected throughout the route
@@ -151,7 +196,7 @@ export class Agent<TContext = unknown> {
151
196
  /**
152
197
  * Create a domain term for the glossary
153
198
  */
154
- createTerm(term: Term): this {
199
+ createTerm(term: Term<TContext>): this {
155
200
  this.terms.push(term);
156
201
  return this;
157
202
  }
@@ -159,7 +204,7 @@ export class Agent<TContext = unknown> {
159
204
  /**
160
205
  * Create a behavioral guideline
161
206
  */
162
- createGuideline(guideline: Guideline): this {
207
+ createGuideline(guideline: Guideline<TContext>): this {
163
208
  const guidelineWithId = {
164
209
  ...guideline,
165
210
  id: guideline.id || `guideline_${this.guidelines.length}`,
@@ -170,50 +215,24 @@ export class Agent<TContext = unknown> {
170
215
  }
171
216
 
172
217
  /**
173
- * Add a capability
218
+ * Register a tool at the agent level
174
219
  */
175
- createCapability(capability: Capability): this {
176
- const capabilityWithId = {
177
- ...capability,
178
- id: capability.id || `capability_${this.capabilities.length}`,
179
- };
180
- this.capabilities.push(capabilityWithId);
220
+ createTool(tool: Tool<TContext, unknown[], unknown, unknown>): this {
221
+ this.tools.push(tool);
181
222
  return this;
182
223
  }
183
224
 
184
225
  /**
185
- * Add a domain with its tools/methods
186
- * Automatically tags all ToolRef objects with their domain name for security enforcement
226
+ * Register multiple tools at the agent level
187
227
  */
188
- addDomain<TName extends string, TDomain extends Record<string, unknown>>(
189
- name: TName,
190
- domainObject: TDomain
191
- ): void {
192
- // Tag all tools in this domain with the domain name for security enforcement
193
- const taggedDomain = { ...domainObject };
194
- for (const key in taggedDomain) {
195
- const value = taggedDomain[key];
196
- // Check if value is a ToolRef (has handler, id, name properties)
197
- if (
198
- value &&
199
- typeof value === "object" &&
200
- "handler" in value &&
201
- "id" in value &&
202
- "name" in value
203
- ) {
204
- // Tag the tool with its domain name
205
- (value as Record<string, unknown>).domainName = name;
206
- }
207
- }
208
-
209
- this.domainRegistry.register(name, taggedDomain);
210
- // Attach to the domain property for easy access
211
- this.domain[name] = taggedDomain;
228
+ registerTools(tools: Tool<TContext, unknown[], unknown, unknown>[]): this {
229
+ tools.forEach((tool) => this.createTool(tool));
230
+ return this;
212
231
  }
213
232
 
214
233
  /**
215
234
  * Update the agent's context
216
- * Triggers the onContextUpdate lifecycle hook if configured
235
+ * Triggers both agent-level and route-specific onContextUpdate lifecycle hooks if configured
217
236
  */
218
237
  async updateContext(updates: Partial<TContext>): Promise<void> {
219
238
  const previousContext = this.context;
@@ -224,7 +243,20 @@ export class Agent<TContext = unknown> {
224
243
  ...(updates as Record<string, unknown>),
225
244
  } as TContext;
226
245
 
227
- // Trigger lifecycle hook if configured
246
+ // Trigger route-specific lifecycle hook if configured and session has current route
247
+ if (this.currentSession?.currentRoute) {
248
+ const currentRoute = this.routes.find(
249
+ (r) => r.id === this.currentSession!.currentRoute?.id
250
+ );
251
+ if (
252
+ currentRoute?.hooks?.onContextUpdate &&
253
+ previousContext !== undefined
254
+ ) {
255
+ await currentRoute.handleContextUpdate(this.context, previousContext);
256
+ }
257
+ }
258
+
259
+ // Trigger agent-level lifecycle hook if configured
228
260
  if (this.options.hooks?.onContextUpdate && previousContext !== undefined) {
229
261
  await this.options.hooks.onContextUpdate(this.context, previousContext);
230
262
  }
@@ -232,22 +264,35 @@ export class Agent<TContext = unknown> {
232
264
 
233
265
  /**
234
266
  * Update collected data in session with lifecycle hook support
235
- * Triggers the onDataUpdate lifecycle hook if configured
267
+ * Triggers both agent-level and route-specific onDataUpdate lifecycle hooks if configured
236
268
  * @internal
237
269
  */
238
270
  private async updateData<TData = unknown>(
239
271
  session: SessionState<TData>,
240
- collectedUpdate: Partial<TData>
272
+ dataUpdate: Partial<TData>
241
273
  ): Promise<SessionState<TData>> {
242
274
  const previousCollected = { ...session.data };
243
275
 
244
276
  // Merge new collected data
245
277
  let newCollected = {
246
278
  ...session.data,
247
- ...collectedUpdate,
279
+ ...dataUpdate,
248
280
  };
249
281
 
250
- // Trigger lifecycle hook if configured
282
+ // Trigger route-specific lifecycle hook if configured and session has a current route
283
+ if (session.currentRoute) {
284
+ const currentRoute = this.routes.find(
285
+ (r) => r.id === session.currentRoute?.id
286
+ );
287
+ if (currentRoute?.hooks?.onDataUpdate) {
288
+ newCollected = await currentRoute.handleDataUpdate(
289
+ newCollected,
290
+ previousCollected
291
+ );
292
+ }
293
+ }
294
+
295
+ // Trigger agent-level lifecycle hook if configured
251
296
  if (this.options.hooks?.onDataUpdate) {
252
297
  newCollected = (await this.options.hooks.onDataUpdate(
253
298
  newCollected,
@@ -261,9 +306,8 @@ export class Agent<TContext = unknown> {
261
306
 
262
307
  /**
263
308
  * Get current context (fetches from provider if configured)
264
- * @internal
265
309
  */
266
- private async getContext(): Promise<TContext | undefined> {
310
+ async getContext(): Promise<TContext | undefined> {
267
311
  // If context provider is configured, use it to fetch fresh context
268
312
  if (this.options.contextProvider) {
269
313
  return await this.options.contextProvider();
@@ -276,193 +320,74 @@ export class Agent<TContext = unknown> {
276
320
  /**
277
321
  * Generate a response based on history and context as a stream
278
322
  */
279
- async *respondStream(params: {
280
- history: Event[];
323
+ async *respondStream<TData = Record<string, unknown>>(params: {
324
+ history: History;
281
325
  step?: StepRef;
282
- session?: SessionState;
326
+ session?: SessionState<TData>;
283
327
  contextOverride?: Partial<TContext>;
284
328
  signal?: AbortSignal;
285
- }): AsyncGenerator<{
286
- delta: string;
287
- accumulated: string;
288
- done: boolean;
289
- session?: SessionState;
290
- toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
291
- isRouteComplete?: boolean;
292
- }> {
293
- const { history, contextOverride, signal } = params;
294
-
295
- // Get current context (may fetch from provider)
296
- let currentContext = await this.getContext();
297
-
298
- // Call beforeRespond hook if configured
299
- if (this.options.hooks?.beforeRespond && currentContext !== undefined) {
300
- currentContext = await this.options.hooks.beforeRespond(currentContext);
301
- // Update stored context with the result from beforeRespond
302
- this.context = currentContext;
303
- }
304
-
305
- // Merge context with override
306
- const effectiveContext = {
307
- ...(currentContext as Record<string, unknown>),
308
- ...(contextOverride as Record<string, unknown>),
309
- } as TContext;
310
-
311
- // Initialize or get session (use current session if available)
312
- let session = params.session || this.currentSession || createSession();
329
+ }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
330
+ const { history: simpleHistory, signal } = params;
331
+ const history = normalizeHistory(simpleHistory);
332
+
333
+ // Prepare context and session using the response pipeline
334
+ this.responsePipeline.setContext(this.context);
335
+ this.responsePipeline.setCurrentSession(this.currentSession);
336
+ let session: SessionState;
337
+ const responseContext = await this.responsePipeline.prepareResponseContext({
338
+ contextOverride: params.contextOverride,
339
+ session: params.session ? cloneDeep(params.session) : undefined,
340
+ });
341
+ const { effectiveContext } = responseContext;
342
+ session = responseContext.session;
343
+ // Update our stored context if it was modified by beforeRespond hook
344
+ this.context = this.responsePipeline.getStoredContext();
313
345
 
314
- // PHASE 1: TOOL EXECUTION - Execute tools if current step has tool
346
+ // PHASE 1: PREPARE - Execute prepare function if current step has one
315
347
  if (session.currentRoute && session.currentStep) {
316
348
  const currentRoute = this.routes.find(
317
349
  (r) => r.id === session.currentRoute?.id
318
350
  );
319
351
  if (currentRoute) {
320
352
  const currentStep = currentRoute.getStep(session.currentStep.id);
321
- if (currentStep) {
322
- const transitions = currentStep.getTransitions();
323
- const toolTransition = transitions.find((t) => t.spec.tool);
324
-
325
- if (toolTransition?.spec.tool) {
326
- const toolExecutor = new ToolExecutor<TContext, unknown>();
327
- // Get allowed domains from current route for security enforcement
328
- const allowedDomains = currentRoute.getDomains();
329
- const result = await toolExecutor.executeTool(
330
- toolTransition.spec.tool as ToolRef<TContext, unknown[], unknown>,
331
- effectiveContext,
332
- this.updateContext.bind(this),
333
- history,
334
- session.data,
335
- allowedDomains
336
- );
337
-
338
- // Update context with tool results
339
- if (result.contextUpdate) {
340
- await this.updateContext(
341
- result.contextUpdate as Partial<TContext>
342
- );
343
- }
344
-
345
- // Update collected data with tool results
346
- if (result.collectedUpdate) {
347
- session = await this.updateData(session, result.collectedUpdate);
348
- logger.debug(
349
- `[Agent] Tool updated collected data:`,
350
- result.collectedUpdate
351
- );
352
- }
353
-
354
- logger.debug(
355
- `[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
356
- );
357
- }
358
- }
359
- }
360
- }
361
-
362
- // PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use (combined)
363
- let selectedRoute: Route<TContext> | undefined;
364
- let responseDirectives: string[] | undefined;
365
- let selectedStep: Step<TContext> | undefined;
366
- let isRouteComplete = false;
367
-
368
- // Check for pending transition from previous route completion
369
- if (session.pendingTransition) {
370
- const targetRoute = this.routes.find(
371
- (r) => r.id === session.pendingTransition?.targetRouteId
372
- );
373
-
374
- if (targetRoute) {
375
- logger.debug(
376
- `[Agent] Auto-transitioning from pending transition to route: ${targetRoute.title}`
377
- );
378
- // Clear pending transition and enter new route
379
- session = {
380
- ...session,
381
- pendingTransition: undefined,
382
- };
383
- session = enterRoute(session, targetRoute.id, targetRoute.title);
384
-
385
- // Merge initial data if available
386
- if (targetRoute.initialData) {
387
- session = mergeCollected(session, targetRoute.initialData);
353
+ if (currentStep?.prepare) {
354
+ logger.debug(`[Agent] Executing prepare for step: ${currentStep.id}`);
355
+ await this.executePrepareFinalize(
356
+ currentStep.prepare,
357
+ effectiveContext,
358
+ session.data,
359
+ currentRoute,
360
+ currentStep
361
+ );
388
362
  }
389
-
390
- selectedRoute = targetRoute;
391
- } else {
392
- logger.warn(
393
- `[Agent] Pending transition target route not found: ${session.pendingTransition.targetRouteId}`
394
- );
395
- // Clear invalid transition
396
- session = {
397
- ...session,
398
- pendingTransition: undefined,
399
- };
400
363
  }
401
364
  }
402
365
 
403
- // If no pending transition or transition handled, do normal routing
404
- if (this.routes.length > 0 && !selectedRoute) {
405
- const orchestration = await this.routingEngine.decideRouteAndStep({
406
- routes: this.routes,
366
+ // PHASE 2: ROUTING + STEP SELECTION - Use response pipeline
367
+ const routingResult =
368
+ await this.responsePipeline.handleRoutingAndStepSelection({
407
369
  session,
408
370
  history,
409
- agentMeta: {
410
- name: this.options.name,
411
- goal: this.options.goal,
412
- description: this.options.description,
413
- personality: this.options.personality,
414
- },
415
- provider: this.options.provider,
416
371
  context: effectiveContext,
417
372
  signal,
418
373
  });
374
+ const selectedRoute = routingResult.selectedRoute;
375
+ const selectedStep = routingResult.selectedStep;
376
+ const responseDirectives = routingResult.responseDirectives;
377
+ const isRouteComplete = routingResult.isRouteComplete;
378
+ session = routingResult.session;
379
+
380
+ // PHASE 3: DETERMINE NEXT STEP - Use pipeline method
381
+ const stepResult = this.responsePipeline.determineNextStep({
382
+ selectedRoute,
383
+ selectedStep,
384
+ session,
385
+ isRouteComplete,
386
+ });
387
+ const nextStep = stepResult.nextStep;
388
+ session = stepResult.session;
419
389
 
420
- selectedRoute = orchestration.selectedRoute;
421
- selectedStep = orchestration.selectedStep;
422
- responseDirectives = orchestration.responseDirectives;
423
- session = orchestration.session;
424
- isRouteComplete = orchestration.isRouteComplete || false;
425
-
426
- // Log if route is complete
427
- if (isRouteComplete) {
428
- logger.debug(
429
- `[Agent] Route complete: all required data collected, END_ROUTE reached`
430
- );
431
- }
432
- }
433
-
434
- // PHASE 3: DETERMINE NEXT STEP - Use step from combined decision or get initial step
435
390
  if (selectedRoute && !isRouteComplete) {
436
- let nextStep: Step<TContext>;
437
-
438
- // If we have a selected step from the combined routing decision, use it
439
- if (selectedStep) {
440
- nextStep = selectedStep;
441
- } else {
442
- // New route or no step selected - get initial step or first valid step
443
- const candidates = this.routingEngine.getCandidateSteps(
444
- selectedRoute,
445
- undefined,
446
- session.data || {}
447
- );
448
- if (candidates.length > 0) {
449
- nextStep = candidates[0].step;
450
- logger.debug(
451
- `[Agent] Using first valid step: ${nextStep.id} for new route`
452
- );
453
- } else {
454
- // Fallback to initial step even if it should be skipped
455
- nextStep = selectedRoute.initialStep;
456
- logger.warn(
457
- `[Agent] No valid steps found, using initial step: ${nextStep.id}`
458
- );
459
- }
460
- }
461
-
462
- // Update session with next step
463
- session = enterStep(session, nextStep.id, nextStep.description);
464
- logger.debug(`[Agent] Entered step: ${nextStep.id}`);
465
-
466
391
  // PHASE 4: RESPONSE GENERATION - Stream message using selected route and step
467
392
  // Get last user message
468
393
  const lastUserMessage = getLastMessageFromHistory(history);
@@ -473,21 +398,42 @@ export class Agent<TContext = unknown> {
473
398
  nextStep
474
399
  );
475
400
 
401
+ // Check if selected route and next step are defined
402
+ if (!selectedRoute || !nextStep) {
403
+ logger.error("[Agent] Selected route or next step is not defined", {
404
+ selectedRoute,
405
+ nextStep,
406
+ });
407
+ throw new Error("Selected route or next step is not defined");
408
+ }
409
+
476
410
  // Build response prompt
477
- const responsePrompt = this.responseEngine.buildResponsePrompt(
478
- selectedRoute,
479
- nextStep,
480
- selectedRoute.getRules(),
481
- selectedRoute.getProhibitions(),
482
- responseDirectives,
411
+ const responsePrompt = await this.responseEngine.buildResponsePrompt({
412
+ route: selectedRoute,
413
+ currentStep: nextStep,
414
+ rules: selectedRoute.getRules(),
415
+ prohibitions: selectedRoute.getProhibitions(),
416
+ directives: responseDirectives,
483
417
  history,
484
- lastUserMessage,
485
- {
486
- name: this.options.name,
487
- goal: this.options.goal,
488
- description: this.options.description,
489
- personality: this.options.personality,
490
- }
418
+ lastMessage: lastUserMessage,
419
+ agentOptions: this.options,
420
+ // Combine agent and route properties according to the specified logic
421
+ combinedGuidelines: [
422
+ ...this.getGuidelines(),
423
+ ...selectedRoute.getGuidelines(),
424
+ ],
425
+ combinedTerms: this.mergeTerms(
426
+ this.getTerms(),
427
+ selectedRoute.getTerms()
428
+ ),
429
+ context: effectiveContext,
430
+ session,
431
+ });
432
+
433
+ // Collect available tools for AI
434
+ const availableTools = this.collectAvailableTools(
435
+ selectedRoute,
436
+ nextStep
491
437
  );
492
438
 
493
439
  // Generate message stream using AI provider
@@ -495,6 +441,7 @@ export class Agent<TContext = unknown> {
495
441
  prompt: responsePrompt,
496
442
  history,
497
443
  context: effectiveContext,
444
+ tools: availableTools,
498
445
  signal,
499
446
  parameters: {
500
447
  jsonSchema: responseSchema,
@@ -504,18 +451,206 @@ export class Agent<TContext = unknown> {
504
451
 
505
452
  // Stream chunks to caller
506
453
  for await (const chunk of stream) {
507
- const toolCalls:
454
+ let toolCalls:
508
455
  | Array<{ toolName: string; arguments: Record<string, unknown> }>
509
456
  | undefined = undefined;
510
457
 
458
+ // Extract tool calls from AI response on final chunk
459
+ if (chunk.done && chunk.structured?.toolCalls) {
460
+ toolCalls = chunk.structured.toolCalls;
461
+
462
+ // Execute dynamic tool calls
463
+ if (toolCalls.length > 0) {
464
+ logger.debug(
465
+ `[Agent] Executing ${toolCalls.length} dynamic tool calls`
466
+ );
467
+
468
+ for (const toolCall of toolCalls) {
469
+ const tool = this.findAvailableTool(
470
+ toolCall.toolName,
471
+ selectedRoute
472
+ );
473
+ if (!tool) {
474
+ logger.warn(`[Agent] Tool not found: ${toolCall.toolName}`);
475
+ continue;
476
+ }
477
+
478
+ const toolExecutor = new ToolExecutor<TContext, unknown>();
479
+ const result = await toolExecutor.executeTool({
480
+ tool: tool,
481
+ context: effectiveContext,
482
+ updateContext: this.updateContext.bind(this),
483
+ history,
484
+ data: session.data,
485
+ toolArguments: toolCall.arguments,
486
+ });
487
+
488
+ // Update context with tool results
489
+ if (result.contextUpdate) {
490
+ await this.updateContext(
491
+ result.contextUpdate as Partial<TContext>
492
+ );
493
+ }
494
+
495
+ // Update collected data with tool results
496
+ if (result.dataUpdate) {
497
+ session = await this.updateData(session, result.dataUpdate);
498
+ logger.debug(
499
+ `[Agent] Tool updated collected data:`,
500
+ result.dataUpdate
501
+ );
502
+ }
503
+
504
+ logger.debug(
505
+ `[Agent] Executed dynamic tool: ${result.toolName} (success: ${result.success})`
506
+ );
507
+ }
508
+ }
509
+ }
510
+
511
+ // TOOL LOOP: Allow AI to make follow-up tool calls after initial tool execution (streaming)
512
+ const MAX_TOOL_LOOPS = 5;
513
+ let toolLoopCount = 0;
514
+ let hasToolCalls = toolCalls && toolCalls.length > 0;
515
+
516
+ while (hasToolCalls && toolLoopCount < MAX_TOOL_LOOPS) {
517
+ toolLoopCount++;
518
+ logger.debug(
519
+ `[Agent] Starting streaming tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS}`
520
+ );
521
+
522
+ // Add tool execution results to history so AI knows what happened
523
+ const toolResultsEvents: Event[] = [];
524
+ for (const toolCall of toolCalls || []) {
525
+ const tool = this.findAvailableTool(
526
+ toolCall.toolName,
527
+ selectedRoute
528
+ );
529
+ if (tool) {
530
+ toolResultsEvents.push({
531
+ kind: EventKind.TOOL,
532
+ source: MessageRole.AGENT,
533
+ timestamp: new Date().toISOString(),
534
+ data: {
535
+ tool_calls: [
536
+ {
537
+ tool_id: toolCall.toolName,
538
+ arguments: toolCall.arguments,
539
+ result: {
540
+ data: "Tool executed successfully",
541
+ },
542
+ },
543
+ ],
544
+ },
545
+ });
546
+ }
547
+ }
548
+
549
+ // Create updated history with tool results
550
+ const updatedHistory = [...history, ...toolResultsEvents];
551
+
552
+ // Make follow-up streaming AI call to see if more tools are needed
553
+ const followUpStream = this.options.provider.generateMessageStream({
554
+ prompt: responsePrompt,
555
+ history: updatedHistory,
556
+ context: effectiveContext,
557
+ tools: availableTools,
558
+ parameters: {
559
+ jsonSchema: responseSchema,
560
+ schemaName: "tool_followup",
561
+ },
562
+ signal,
563
+ });
564
+
565
+ let followUpToolCalls:
566
+ | Array<{ toolName: string; arguments: Record<string, unknown> }>
567
+ | undefined;
568
+
569
+ for await (const followUpChunk of followUpStream) {
570
+ // Extract tool calls from follow-up stream
571
+ if (followUpChunk.done && followUpChunk.structured?.toolCalls) {
572
+ followUpToolCalls = followUpChunk.structured.toolCalls;
573
+ }
574
+ }
575
+
576
+ hasToolCalls = followUpToolCalls && followUpToolCalls.length > 0;
577
+
578
+ if (hasToolCalls) {
579
+ logger.debug(
580
+ `[Agent] Follow-up streaming call produced ${
581
+ followUpToolCalls!.length
582
+ } additional tool calls`
583
+ );
584
+
585
+ // Execute the follow-up tool calls
586
+ for (const toolCall of followUpToolCalls!) {
587
+ const tool = this.findAvailableTool(
588
+ toolCall.toolName,
589
+ selectedRoute
590
+ );
591
+ if (!tool) {
592
+ logger.warn(
593
+ `[Agent] Tool not found in streaming follow-up: ${toolCall.toolName}`
594
+ );
595
+ continue;
596
+ }
597
+
598
+ const toolExecutor = new ToolExecutor<TContext, unknown>();
599
+ const result = await toolExecutor.executeTool({
600
+ tool: tool,
601
+ context: effectiveContext,
602
+ updateContext: this.updateContext.bind(this),
603
+ history: updatedHistory,
604
+ data: session.data,
605
+ toolArguments: toolCall.arguments,
606
+ });
607
+
608
+ // Update context with follow-up tool results
609
+ if (result.contextUpdate) {
610
+ await this.updateContext(
611
+ result.contextUpdate as Partial<TContext>
612
+ );
613
+ }
614
+
615
+ if (result.dataUpdate) {
616
+ session = await this.updateData(session, result.dataUpdate);
617
+ logger.debug(
618
+ `[Agent] Streaming follow-up tool updated collected data:`,
619
+ result.dataUpdate
620
+ );
621
+ }
622
+
623
+ logger.debug(
624
+ `[Agent] Executed streaming follow-up tool: ${result.toolName} (success: ${result.success})`
625
+ );
626
+ }
627
+
628
+ // Update toolCalls for next iteration
629
+ toolCalls = followUpToolCalls;
630
+ } else {
631
+ logger.debug(
632
+ `[Agent] Streaming tool loop completed after ${toolLoopCount} iterations`
633
+ );
634
+ // Update toolCalls for final response
635
+ toolCalls = followUpToolCalls || [];
636
+ break;
637
+ }
638
+ }
639
+
640
+ if (toolLoopCount >= MAX_TOOL_LOOPS) {
641
+ logger.warn(
642
+ `[Agent] Streaming tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`
643
+ );
644
+ }
645
+
511
646
  // Extract collected data on final chunk
512
- if (chunk.done && chunk.structured && nextStep.collectFields) {
647
+ if (chunk.done && chunk.structured && nextStep.collect) {
513
648
  const collectedData: Record<string, unknown> = {};
514
649
  // The structured response includes both base fields and collected extraction fields
515
650
  const structuredData = chunk.structured as AgentStructuredResponse &
516
651
  Record<string, unknown>;
517
652
 
518
- for (const field of nextStep.collectFields) {
653
+ for (const field of nextStep.collect) {
519
654
  if (field in structuredData) {
520
655
  collectedData[field] = structuredData[field];
521
656
  }
@@ -554,6 +689,28 @@ export class Agent<TContext = unknown> {
554
689
  );
555
690
  }
556
691
 
692
+ // Execute finalize function on final chunk
693
+ if (chunk.done && session.currentRoute && session.currentStep) {
694
+ const currentRoute = this.routes.find(
695
+ (r) => r.id === session.currentRoute?.id
696
+ );
697
+ if (currentRoute) {
698
+ const currentStep = currentRoute.getStep(session.currentStep.id);
699
+ if (currentStep?.finalize) {
700
+ logger.debug(
701
+ `[Agent] Executing finalize for step: ${currentStep.id}`
702
+ );
703
+ await this.executePrepareFinalize(
704
+ currentStep.finalize,
705
+ effectiveContext,
706
+ session.data,
707
+ currentRoute,
708
+ currentStep
709
+ );
710
+ }
711
+ }
712
+ }
713
+
557
714
  // Update current session if we have one
558
715
  if (chunk.done && this.currentSession) {
559
716
  this.currentSession = session;
@@ -566,6 +723,8 @@ export class Agent<TContext = unknown> {
566
723
  session, // Return updated session
567
724
  toolCalls,
568
725
  isRouteComplete,
726
+ metadata: chunk.metadata,
727
+ structured: chunk.structured,
569
728
  };
570
729
  }
571
730
  } else if (isRouteComplete && selectedRoute) {
@@ -576,40 +735,49 @@ export class Agent<TContext = unknown> {
576
735
  const endStepSpec = selectedRoute.endStepSpec;
577
736
 
578
737
  // Create a temporary step for completion message generation using endStep configuration
579
- const completionStep = new Step<TContext>(
580
- selectedRoute.id,
581
- endStepSpec.instructions ||
582
- "Summarize what was accomplished and confirm completion",
583
- endStepSpec.id || END_ROUTE_ID,
584
- endStepSpec.collect,
585
- undefined,
586
- endStepSpec.requires,
587
- endStepSpec.instructions ||
588
- "Summarize what was accomplished and confirm completion based on the conversation history and collected data"
589
- );
738
+ const completionStep = new Step<TContext, unknown>(selectedRoute.id, {
739
+ description: endStepSpec.description,
740
+ id: endStepSpec.id || END_ROUTE_ID,
741
+ collect: endStepSpec.collect,
742
+ requires: endStepSpec.requires,
743
+ prompt:
744
+ endStepSpec.prompt ||
745
+ "Summarize what was accomplished and confirm completion based on the conversation history and collected data",
746
+ });
590
747
 
591
748
  // Build response schema for completion
592
749
  const responseSchema = this.responseEngine.responseSchemaForRoute(
593
750
  selectedRoute,
594
751
  completionStep
595
752
  );
753
+ const templateContext = {
754
+ context: effectiveContext,
755
+ session,
756
+ history,
757
+ };
596
758
 
597
759
  // Build completion response prompt
598
- const completionPrompt = this.responseEngine.buildResponsePrompt(
599
- selectedRoute,
600
- completionStep,
601
- selectedRoute.getRules(),
602
- selectedRoute.getProhibitions(),
603
- undefined, // No directives for completion
760
+ const completionPrompt = await this.responseEngine.buildResponsePrompt({
761
+ route: selectedRoute,
762
+ currentStep: completionStep,
763
+ rules: selectedRoute.getRules(),
764
+ prohibitions: selectedRoute.getProhibitions(),
765
+ directives: undefined, // No directives for completion
604
766
  history,
605
- lastUserMessage,
606
- {
607
- name: this.options.name,
608
- goal: this.options.goal,
609
- description: this.options.description,
610
- personality: this.options.personality,
611
- }
612
- );
767
+ lastMessage: lastUserMessage,
768
+ agentOptions: this.options,
769
+ // Combine agent and route properties according to the specified logic
770
+ combinedGuidelines: [
771
+ ...this.getGuidelines(),
772
+ ...selectedRoute.getGuidelines(),
773
+ ],
774
+ combinedTerms: this.mergeTerms(
775
+ this.getTerms(),
776
+ selectedRoute.getTerms()
777
+ ),
778
+ context: effectiveContext,
779
+ session,
780
+ });
613
781
 
614
782
  // Stream completion message using AI provider
615
783
  const stream = this.options.provider.generateMessageStream({
@@ -642,12 +810,16 @@ export class Agent<TContext = unknown> {
642
810
  );
643
811
 
644
812
  if (targetRoute) {
813
+ const renderedCondition = await render(
814
+ transitionConfig.condition,
815
+ templateContext
816
+ );
645
817
  // Set pending transition in session
646
818
  session = {
647
819
  ...session,
648
820
  pendingTransition: {
649
821
  targetRouteId: targetRoute.id,
650
- condition: transitionConfig.condition,
822
+ condition: renderedCondition,
651
823
  reason: "route_complete",
652
824
  },
653
825
  };
@@ -681,22 +853,20 @@ export class Agent<TContext = unknown> {
681
853
  session,
682
854
  toolCalls: undefined,
683
855
  isRouteComplete: true,
856
+ metadata: chunk.metadata,
857
+ structured: chunk.structured,
684
858
  };
685
859
  }
686
860
  } else {
687
861
  // Fallback: No routes defined, stream a simple response
688
- const fallbackPrompt = new PromptComposer<TContext>()
689
- .addAgentMeta({
690
- name: this.options.name,
691
- goal: this.options.goal,
692
- description: this.options.description,
693
- })
694
- .addPersonality(this.options.personality)
695
- .addInteractionHistory(history)
696
- .addGlossary(this.terms)
697
- .addGuidelines(this.guidelines)
698
- .addCapabilities(this.capabilities)
699
- .build();
862
+ const fallbackPrompt = await this.responseEngine.buildFallbackPrompt({
863
+ history,
864
+ agentOptions: this.options,
865
+ terms: this.terms,
866
+ guidelines: this.guidelines,
867
+ context: effectiveContext,
868
+ session,
869
+ });
700
870
 
701
871
  const stream = this.options.provider.generateMessageStream({
702
872
  prompt: fallbackPrompt,
@@ -729,6 +899,8 @@ export class Agent<TContext = unknown> {
729
899
  session, // Return updated session
730
900
  toolCalls: undefined,
731
901
  isRouteComplete: false,
902
+ metadata: chunk.metadata,
903
+ structured: chunk.structured,
732
904
  };
733
905
  }
734
906
  }
@@ -737,19 +909,15 @@ export class Agent<TContext = unknown> {
737
909
  /**
738
910
  * Generate a response based on history and context
739
911
  */
740
- async respond(params: {
741
- history: Event[];
912
+ async respond<TData = Record<string, unknown>>(params: {
913
+ history: History;
742
914
  step?: StepRef;
743
- session?: SessionState;
915
+ session?: SessionState<TData>;
744
916
  contextOverride?: Partial<TContext>;
745
917
  signal?: AbortSignal;
746
- }): Promise<{
747
- message: string;
748
- session?: SessionState;
749
- toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
750
- isRouteComplete?: boolean;
751
- }> {
752
- const { history, contextOverride, signal } = params;
918
+ }): Promise<AgentResponse<TData>> {
919
+ const { history: simpleHistory, contextOverride, signal } = params;
920
+ const history = normalizeHistory(simpleHistory);
753
921
 
754
922
  // Get current context (may fetch from provider)
755
923
  let currentContext = await this.getContext();
@@ -769,60 +937,34 @@ export class Agent<TContext = unknown> {
769
937
 
770
938
  // Initialize or get session (use current session if available)
771
939
  let session =
772
- params.session || this.currentSession || createSession<TContext>();
940
+ cloneDeep(params.session) ||
941
+ cloneDeep(this.currentSession) ||
942
+ (await this.session.getOrCreate());
773
943
 
774
- // PHASE 1: TOOL EXECUTION - Execute tools if current step has tool
944
+ // PHASE 1: PREPARE - Execute prepare function if current step has one
775
945
  if (session.currentRoute && session.currentStep) {
776
946
  const currentRoute = this.routes.find(
777
947
  (r) => r.id === session.currentRoute?.id
778
948
  );
779
949
  if (currentRoute) {
780
950
  const currentStep = currentRoute.getStep(session.currentStep.id);
781
- if (currentStep) {
782
- const transitions = currentStep.getTransitions();
783
- const toolTransition = transitions.find((t) => t.spec.tool);
784
-
785
- if (toolTransition?.spec.tool) {
786
- const toolExecutor = new ToolExecutor<TContext, unknown>();
787
- // Get allowed domains from current route for security enforcement
788
- const allowedDomains = currentRoute.getDomains();
789
- const result = await toolExecutor.executeTool(
790
- toolTransition.spec.tool as ToolRef<TContext, unknown[], unknown>,
791
- effectiveContext,
792
- this.updateContext.bind(this),
793
- history,
794
- session.data,
795
- allowedDomains
796
- );
797
-
798
- // Update context with tool results
799
- if (result.contextUpdate) {
800
- await this.updateContext(
801
- result.contextUpdate as Partial<TContext>
802
- );
803
- }
804
-
805
- // Update collected data with tool results
806
- if (result.collectedUpdate) {
807
- session = await this.updateData(session, result.collectedUpdate);
808
- logger.debug(
809
- `[Agent] Tool updated collected data:`,
810
- result.collectedUpdate
811
- );
812
- }
813
-
814
- logger.debug(
815
- `[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
816
- );
817
- }
951
+ if (currentStep?.prepare) {
952
+ logger.debug(`[Agent] Executing prepare for step: ${currentStep.id}`);
953
+ await this.executePrepareFinalize(
954
+ currentStep.prepare,
955
+ effectiveContext,
956
+ session.data,
957
+ currentRoute,
958
+ currentStep
959
+ );
818
960
  }
819
961
  }
820
962
  }
821
963
 
822
964
  // PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use (combined)
823
- let selectedRoute: Route<TContext> | undefined;
965
+ let selectedRoute: Route<TContext, unknown> | undefined;
824
966
  let responseDirectives: string[] | undefined;
825
- let selectedStep: Step<TContext> | undefined;
967
+ let selectedStep: Step<TContext, unknown> | undefined;
826
968
  let isRouteComplete = false;
827
969
 
828
970
  // Check for pending transition from previous route completion
@@ -866,12 +1008,7 @@ export class Agent<TContext = unknown> {
866
1008
  routes: this.routes,
867
1009
  session,
868
1010
  history,
869
- agentMeta: {
870
- name: this.options.name,
871
- goal: this.options.goal,
872
- description: this.options.description,
873
- personality: this.options.personality,
874
- },
1011
+ agentOptions: this.options,
875
1012
  provider: this.options.provider,
876
1013
  context: effectiveContext,
877
1014
  signal,
@@ -893,13 +1030,18 @@ export class Agent<TContext = unknown> {
893
1030
 
894
1031
  // PHASE 3: DETERMINE NEXT STEP - Use step from combined decision or get initial step
895
1032
  let message: string;
896
- const toolCalls:
1033
+ let toolCalls:
897
1034
  | Array<{ toolName: string; arguments: Record<string, unknown> }>
898
1035
  | undefined = undefined;
1036
+ let responsePrompt: string;
1037
+ let availableTools: Tool<TContext, unknown[], unknown, unknown>[] = [];
1038
+ let responseSchema: StructuredSchema | undefined;
1039
+ let nextStep: Step<TContext, unknown> | undefined;
899
1040
 
900
- if (selectedRoute && !isRouteComplete) {
901
- let nextStep: Step<TContext>;
1041
+ // Get last user message (needed for both route and completion handling)
1042
+ const lastUserMessage = getLastMessageFromHistory(history);
902
1043
 
1044
+ if (selectedRoute && !isRouteComplete) {
903
1045
  // If we have a selected step from the combined routing decision, use it
904
1046
  if (selectedStep) {
905
1047
  nextStep = selectedStep;
@@ -933,115 +1075,332 @@ export class Agent<TContext = unknown> {
933
1075
  const lastUserMessage = getLastMessageFromHistory(history);
934
1076
 
935
1077
  // Build response schema for this route (with collect fields from step)
936
- const responseSchema = this.responseEngine.responseSchemaForRoute(
1078
+ responseSchema = this.responseEngine.responseSchemaForRoute(
937
1079
  selectedRoute,
938
1080
  nextStep
939
1081
  );
940
1082
 
941
1083
  // Build response prompt
942
- const responsePrompt = this.responseEngine.buildResponsePrompt(
1084
+ responsePrompt = await this.responseEngine.buildResponsePrompt({
1085
+ route: selectedRoute,
1086
+ currentStep: nextStep,
1087
+ rules: selectedRoute.getRules(),
1088
+ prohibitions: selectedRoute.getProhibitions(),
1089
+ directives: responseDirectives,
1090
+ history,
1091
+ lastMessage: lastUserMessage,
1092
+ agentOptions: this.options,
1093
+ // Combine agent and route properties according to the specified logic
1094
+ combinedGuidelines: [
1095
+ ...this.getGuidelines(),
1096
+ ...selectedRoute.getGuidelines(),
1097
+ ],
1098
+ combinedTerms: this.mergeTerms(
1099
+ this.getTerms(),
1100
+ selectedRoute.getTerms()
1101
+ ),
1102
+ context: effectiveContext,
1103
+ session,
1104
+ });
1105
+
1106
+ // Collect available tools for AI
1107
+ availableTools = this.collectAvailableTools(
943
1108
  selectedRoute,
944
- nextStep,
945
- selectedRoute.getRules(),
946
- selectedRoute.getProhibitions(),
947
- responseDirectives,
1109
+ nextStep
1110
+ ) as Tool<TContext, unknown[], unknown, unknown>[];
1111
+ } else {
1112
+ // No route selected - generate basic response without route context
1113
+ logger.debug(`[Agent] No route selected, generating basic response`);
1114
+
1115
+ // Build basic response prompt without route context
1116
+ responsePrompt = await this.responseEngine.buildFallbackPrompt({
948
1117
  history,
949
- lastUserMessage,
950
- {
951
- name: this.options.name,
952
- goal: this.options.goal,
953
- description: this.options.description,
954
- personality: this.options.personality,
1118
+ agentOptions: this.options,
1119
+ terms: this.getTerms(),
1120
+ guidelines: this.getGuidelines(),
1121
+ context: effectiveContext,
1122
+ session,
1123
+ });
1124
+
1125
+ // Use agent-level tools only
1126
+ availableTools = [...this.tools];
1127
+ responseSchema = undefined;
1128
+ }
1129
+
1130
+ // Generate message using AI provider (common for both route and no-route cases)
1131
+ const result = await this.options.provider.generateMessage({
1132
+ prompt: responsePrompt,
1133
+ history,
1134
+ context: effectiveContext,
1135
+ tools: availableTools,
1136
+ signal,
1137
+ parameters: responseSchema
1138
+ ? {
1139
+ jsonSchema: responseSchema,
1140
+ schemaName: "response_output",
1141
+ }
1142
+ : undefined,
1143
+ });
1144
+
1145
+ message = result.structured?.message || result.message;
1146
+
1147
+ // Process dynamic tool calls from AI response (common for both route and no-route cases)
1148
+ if (result.structured?.toolCalls) {
1149
+ toolCalls = result.structured.toolCalls;
1150
+
1151
+ // Execute dynamic tool calls
1152
+ if (toolCalls.length > 0) {
1153
+ logger.debug(
1154
+ `[Agent] Executing ${toolCalls.length} dynamic tool calls`
1155
+ );
1156
+
1157
+ for (const toolCall of toolCalls) {
1158
+ const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
1159
+ if (!tool) {
1160
+ logger.warn(`[Agent] Tool not found: ${toolCall.toolName}`);
1161
+ continue;
1162
+ }
1163
+
1164
+ const toolExecutor = new ToolExecutor<TContext, unknown>();
1165
+ const toolResult = await toolExecutor.executeTool({
1166
+ tool: tool,
1167
+ context: effectiveContext,
1168
+ updateContext: this.updateContext.bind(this),
1169
+ history,
1170
+ data: session.data,
1171
+ toolArguments: toolCall.arguments,
1172
+ });
1173
+
1174
+ // Update context with tool results
1175
+ if (toolResult.contextUpdate) {
1176
+ await this.updateContext(
1177
+ toolResult.contextUpdate as Partial<TContext>
1178
+ );
1179
+ }
1180
+
1181
+ // Update collected data with tool results
1182
+ if (toolResult.dataUpdate) {
1183
+ session = await this.updateData(session, toolResult.dataUpdate);
1184
+ logger.debug(
1185
+ `[Agent] Tool updated collected data:`,
1186
+ toolResult.dataUpdate
1187
+ );
1188
+ }
1189
+
1190
+ logger.debug(
1191
+ `[Agent] Executed dynamic tool: ${toolResult.toolName} (success: ${toolResult.success})`
1192
+ );
955
1193
  }
1194
+ }
1195
+ }
1196
+
1197
+ // TOOL LOOP: Allow AI to make follow-up tool calls after initial tool execution
1198
+ const MAX_TOOL_LOOPS = 5;
1199
+ let toolLoopCount = 0;
1200
+ let hasToolCalls = toolCalls && toolCalls.length > 0;
1201
+
1202
+ while (hasToolCalls && toolLoopCount < MAX_TOOL_LOOPS) {
1203
+ toolLoopCount++;
1204
+ logger.debug(
1205
+ `[Agent] Starting tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS}`
956
1206
  );
957
1207
 
958
- // Generate message using AI provider
959
- const result = await this.options.provider.generateMessage({
1208
+ // Add tool execution results to history so AI knows what happened
1209
+ const toolResultsEvents: Event[] = [];
1210
+ for (const toolCall of toolCalls || []) {
1211
+ const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
1212
+ if (tool) {
1213
+ toolResultsEvents.push({
1214
+ kind: EventKind.TOOL,
1215
+ source: MessageRole.AGENT,
1216
+ timestamp: new Date().toISOString(),
1217
+ data: {
1218
+ tool_calls: [
1219
+ {
1220
+ tool_id: toolCall.toolName,
1221
+ arguments: toolCall.arguments,
1222
+ result: {
1223
+ data: "Tool executed successfully",
1224
+ },
1225
+ },
1226
+ ],
1227
+ },
1228
+ });
1229
+ }
1230
+ }
1231
+
1232
+ // Create updated history with tool results
1233
+ const updatedHistory = [...history, ...toolResultsEvents];
1234
+
1235
+ // Make follow-up AI call to see if more tools are needed
1236
+ const followUpResult = await this.options.provider.generateMessage({
960
1237
  prompt: responsePrompt,
961
- history,
1238
+ history: updatedHistory,
962
1239
  context: effectiveContext,
963
- signal,
1240
+ tools: availableTools,
964
1241
  parameters: {
965
- jsonSchema: responseSchema,
966
- schemaName: "response_output",
1242
+ jsonSchema: responseSchema as StructuredSchema,
1243
+ schemaName: "tool_followup",
967
1244
  },
1245
+ signal,
968
1246
  });
969
1247
 
970
- message = result.structured?.message || result.message;
1248
+ // Check if follow-up call has more tool calls
1249
+ const followUpToolCalls = followUpResult.structured?.toolCalls;
1250
+ hasToolCalls = followUpToolCalls && followUpToolCalls.length > 0;
1251
+
1252
+ if (hasToolCalls) {
1253
+ logger.debug(
1254
+ `[Agent] Follow-up call produced ${
1255
+ followUpToolCalls!.length
1256
+ } additional tool calls`
1257
+ );
971
1258
 
972
- // Extract collected data from response
973
- if (result.structured && nextStep.collectFields) {
974
- const collectedData: Record<string, unknown> = {};
975
- // The structured response includes both base fields and collected extraction fields
976
- const structuredData = result.structured as AgentStructuredResponse &
977
- Record<string, unknown>;
1259
+ // Execute the follow-up tool calls
1260
+ for (const toolCall of followUpToolCalls!) {
1261
+ const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
1262
+ if (!tool) {
1263
+ logger.warn(
1264
+ `[Agent] Tool not found in follow-up: ${toolCall.toolName}`
1265
+ );
1266
+ continue;
1267
+ }
978
1268
 
979
- for (const field of nextStep.collectFields) {
980
- if (field in structuredData) {
981
- collectedData[field] = structuredData[field];
1269
+ const toolExecutor = new ToolExecutor<TContext, unknown>();
1270
+ const toolResult = await toolExecutor.executeTool({
1271
+ tool: tool,
1272
+ context: effectiveContext,
1273
+ updateContext: this.updateContext.bind(this),
1274
+ history: updatedHistory,
1275
+ data: session.data,
1276
+ toolArguments: toolCall.arguments,
1277
+ });
1278
+
1279
+ // Update context with follow-up tool results
1280
+ if (toolResult.contextUpdate) {
1281
+ await this.updateContext(
1282
+ toolResult.contextUpdate as Partial<TContext>
1283
+ );
1284
+ }
1285
+
1286
+ if (toolResult.dataUpdate) {
1287
+ session = await this.updateData(session, toolResult.dataUpdate);
1288
+ logger.debug(
1289
+ `[Agent] Follow-up tool updated collected data:`,
1290
+ toolResult.dataUpdate
1291
+ );
982
1292
  }
1293
+
1294
+ logger.debug(
1295
+ `[Agent] Executed follow-up tool: ${toolResult.toolName} (success: ${toolResult.success})`
1296
+ );
983
1297
  }
984
1298
 
985
- // Merge collected data into session
986
- if (Object.keys(collectedData).length > 0) {
987
- session = await this.updateData(session, collectedData);
988
- logger.debug(`[Agent] Collected data:`, collectedData);
1299
+ // Update toolCalls for next iteration or final response
1300
+ toolCalls = followUpToolCalls;
1301
+ } else {
1302
+ logger.debug(
1303
+ `[Agent] Tool loop completed after ${toolLoopCount} iterations`
1304
+ );
1305
+ // Update final message and toolCalls from follow-up result if no more tools
1306
+ message = followUpResult.structured?.message || followUpResult.message;
1307
+ toolCalls = followUpToolCalls || [];
1308
+ break;
1309
+ }
1310
+ }
1311
+
1312
+ if (toolLoopCount >= MAX_TOOL_LOOPS) {
1313
+ logger.warn(
1314
+ `[Agent] Tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`
1315
+ );
1316
+ }
1317
+
1318
+ // Extract collected data from final response (only for route-based interactions)
1319
+ if (selectedRoute && result.structured && nextStep?.collect) {
1320
+ const collectedData: Record<string, unknown> = {};
1321
+ // The structured response includes both base fields and collected extraction fields
1322
+ const structuredData = result.structured as AgentStructuredResponse &
1323
+ Record<string, unknown>;
1324
+
1325
+ for (const field of nextStep.collect) {
1326
+ if (field in structuredData) {
1327
+ collectedData[field] = structuredData[field];
989
1328
  }
990
1329
  }
991
1330
 
992
- // Extract any additional data from structured response
993
- if (
994
- result.structured &&
995
- typeof result.structured === "object" &&
996
- "contextUpdate" in result.structured
997
- ) {
998
- await this.updateContext(
999
- (result.structured as { contextUpdate?: Partial<TContext> })
1000
- .contextUpdate as Partial<TContext>
1001
- );
1331
+ // Merge collected data into session
1332
+ if (Object.keys(collectedData).length > 0) {
1333
+ session = await this.updateData(session, collectedData);
1334
+ logger.debug(`[Agent] Collected data:`, collectedData);
1002
1335
  }
1003
- } else if (isRouteComplete && selectedRoute) {
1336
+ }
1337
+
1338
+ // Extract any additional data from structured response
1339
+ if (
1340
+ result.structured &&
1341
+ typeof result.structured === "object" &&
1342
+ "contextUpdate" in result.structured
1343
+ ) {
1344
+ await this.updateContext(
1345
+ (result.structured as { contextUpdate?: Partial<TContext> })
1346
+ .contextUpdate as Partial<TContext>
1347
+ );
1348
+ }
1349
+
1350
+ // Handle route completion if route is complete
1351
+ if (isRouteComplete) {
1004
1352
  // Route is complete - generate completion message then check for onComplete transition
1005
- const lastUserMessage = getLastMessageFromHistory(history);
1006
1353
 
1007
1354
  // Get endStep spec from route
1008
- const endStepSpec = selectedRoute.endStepSpec;
1355
+ const endStepSpec = selectedRoute!.endStepSpec;
1009
1356
 
1010
1357
  // Create a temporary step for completion message generation using endStep configuration
1011
- const completionStep = new Step<TContext>(
1012
- selectedRoute.id,
1013
- endStepSpec.instructions ||
1014
- "Summarize what was accomplished and confirm completion",
1015
- endStepSpec.id || END_ROUTE_ID,
1016
- endStepSpec.collect,
1017
- undefined,
1018
- endStepSpec.requires,
1019
- endStepSpec.instructions ||
1020
- "Summarize what was accomplished and confirm completion based on the conversation history and collected data"
1021
- );
1358
+ const completionStep = new Step<TContext, unknown>(selectedRoute!.id, {
1359
+ description: endStepSpec.description,
1360
+ id: endStepSpec.id || END_ROUTE_ID,
1361
+ collect: endStepSpec.collect,
1362
+ requires: endStepSpec.requires,
1363
+ prompt:
1364
+ endStepSpec.prompt ||
1365
+ "Summarize what was accomplished and confirm completion based on the conversation history and collected data",
1366
+ });
1022
1367
 
1023
1368
  // Build response schema for completion
1024
1369
  const responseSchema = this.responseEngine.responseSchemaForRoute(
1025
- selectedRoute,
1370
+ selectedRoute!,
1026
1371
  completionStep
1027
1372
  );
1373
+ const templateContext = {
1374
+ context: effectiveContext,
1375
+ session,
1376
+ history,
1377
+ };
1378
+ if (!selectedRoute) {
1379
+ throw new Error("Selected route is not defined");
1380
+ }
1028
1381
 
1029
1382
  // Build completion response prompt
1030
- const completionPrompt = this.responseEngine.buildResponsePrompt(
1031
- selectedRoute,
1032
- completionStep,
1033
- selectedRoute.getRules(),
1034
- selectedRoute.getProhibitions(),
1035
- undefined, // No directives for completion
1383
+ const completionPrompt = await this.responseEngine.buildResponsePrompt({
1384
+ route: selectedRoute,
1385
+ currentStep: completionStep,
1386
+ rules: selectedRoute.getRules(),
1387
+ prohibitions: selectedRoute.getProhibitions(),
1388
+ directives: undefined, // No directives for completion
1036
1389
  history,
1037
- lastUserMessage,
1038
- {
1039
- name: this.options.name,
1040
- goal: this.options.goal,
1041
- description: this.options.description,
1042
- personality: this.options.personality,
1043
- }
1044
- );
1390
+ lastMessage: lastUserMessage,
1391
+ agentOptions: this.options,
1392
+ // Combine agent and route properties according to the specified logic
1393
+ combinedGuidelines: [
1394
+ ...this.getGuidelines(),
1395
+ ...selectedRoute.getGuidelines(),
1396
+ ],
1397
+ combinedTerms: this.mergeTerms(
1398
+ this.getTerms(),
1399
+ selectedRoute.getTerms()
1400
+ ),
1401
+ context: effectiveContext,
1402
+ session,
1403
+ });
1045
1404
 
1046
1405
  // Generate completion message using AI provider
1047
1406
  const completionResult = await this.options.provider.generateMessage({
@@ -1076,12 +1435,16 @@ export class Agent<TContext = unknown> {
1076
1435
  );
1077
1436
 
1078
1437
  if (targetRoute) {
1438
+ const renderedCondition = await render(
1439
+ transitionConfig.condition,
1440
+ templateContext
1441
+ );
1079
1442
  // Set pending transition in session
1080
1443
  session = {
1081
1444
  ...session,
1082
1445
  pendingTransition: {
1083
1446
  targetRouteId: targetRoute.id,
1084
- condition: transitionConfig.condition,
1447
+ condition: renderedCondition,
1085
1448
  reason: "route_complete",
1086
1449
  },
1087
1450
  };
@@ -1102,18 +1465,14 @@ export class Agent<TContext = unknown> {
1102
1465
  );
1103
1466
  } else {
1104
1467
  // Fallback: No routes defined, generate a simple response
1105
- const fallbackPrompt = new PromptComposer<TContext>()
1106
- .addAgentMeta({
1107
- name: this.options.name,
1108
- goal: this.options.goal,
1109
- description: this.options.description,
1110
- })
1111
- .addPersonality(this.options.personality)
1112
- .addInteractionHistory(history)
1113
- .addGlossary(this.terms)
1114
- .addGuidelines(this.guidelines)
1115
- .addCapabilities(this.capabilities)
1116
- .build();
1468
+ const fallbackPrompt = await this.responseEngine.buildFallbackPrompt({
1469
+ history,
1470
+ agentOptions: this.options,
1471
+ terms: this.terms,
1472
+ guidelines: this.guidelines,
1473
+ context: effectiveContext,
1474
+ session,
1475
+ });
1117
1476
 
1118
1477
  const result = await this.options.provider.generateMessage({
1119
1478
  prompt: fallbackPrompt,
@@ -1148,6 +1507,28 @@ export class Agent<TContext = unknown> {
1148
1507
  );
1149
1508
  }
1150
1509
 
1510
+ // Execute finalize function
1511
+ if (session.currentRoute && session.currentStep) {
1512
+ const currentRoute = this.routes.find(
1513
+ (r) => r.id === session.currentRoute?.id
1514
+ );
1515
+ if (currentRoute) {
1516
+ const currentStep = currentRoute.getStep(session.currentStep.id);
1517
+ if (currentStep?.finalize) {
1518
+ logger.debug(
1519
+ `[Agent] Executing finalize for step: ${currentStep.id}`
1520
+ );
1521
+ await this.executePrepareFinalize(
1522
+ currentStep.finalize,
1523
+ effectiveContext,
1524
+ session.data,
1525
+ currentRoute,
1526
+ currentStep
1527
+ );
1528
+ }
1529
+ }
1530
+ }
1531
+
1151
1532
  // Update current session if we have one
1152
1533
  if (this.currentSession) {
1153
1534
  this.currentSession = session;
@@ -1171,79 +1552,261 @@ export class Agent<TContext = unknown> {
1171
1552
  /**
1172
1553
  * Get all terms
1173
1554
  */
1174
- getTerms(): Term[] {
1555
+ getTerms(): Term<TContext>[] {
1175
1556
  return [...this.terms];
1176
1557
  }
1177
1558
 
1178
1559
  /**
1179
- * Get all guidelines
1560
+ * Get all tools
1180
1561
  */
1181
- getGuidelines(): Guideline[] {
1182
- return [...this.guidelines];
1562
+ getTools(): Tool<TContext, unknown[], unknown, unknown>[] {
1563
+ return [...this.tools];
1183
1564
  }
1184
1565
 
1185
1566
  /**
1186
- * Get all capabilities
1567
+ * Find an available tool by name for the given route
1568
+ * Route-level tools take precedence over agent-level tools
1569
+ * @private
1187
1570
  */
1188
- getCapabilities(): Capability[] {
1189
- return [...this.capabilities];
1571
+ private findAvailableTool(
1572
+ toolName: string,
1573
+ route?: Route<TContext, unknown>
1574
+ ): Tool<TContext, unknown[], unknown, unknown> | undefined {
1575
+ // Check route-level tools first (if route provided)
1576
+ if (route) {
1577
+ const routeTool = route
1578
+ .getTools()
1579
+ .find((tool) => tool.id === toolName || tool.name === toolName);
1580
+ if (routeTool) return routeTool;
1581
+ }
1582
+
1583
+ // Fall back to agent-level tools
1584
+ return this.tools.find(
1585
+ (tool) => tool.id === toolName || tool.name === toolName
1586
+ );
1190
1587
  }
1191
1588
 
1192
1589
  /**
1193
- * Get the domain registry
1590
+ * Collect all available tools for the given route and step context
1591
+ * @private
1194
1592
  */
1195
- getDomainRegistry(): DomainRegistry {
1196
- return this.domainRegistry;
1593
+ private collectAvailableTools(
1594
+ route?: Route<TContext, unknown>,
1595
+ step?: Step<TContext, unknown>
1596
+ ): Array<{
1597
+ id: string;
1598
+ name: string;
1599
+ description?: string;
1600
+ parameters?: unknown;
1601
+ }> {
1602
+ const availableTools = new Map<
1603
+ string,
1604
+ Tool<TContext, unknown[], unknown, unknown>
1605
+ >();
1606
+
1607
+ // Add agent-level tools
1608
+ this.tools.forEach((tool) => {
1609
+ availableTools.set(tool.id, tool);
1610
+ });
1611
+
1612
+ // Add route-level tools (these take precedence)
1613
+ if (route) {
1614
+ route.getTools().forEach((tool) => {
1615
+ availableTools.set(tool.id, tool);
1616
+ });
1617
+ }
1618
+
1619
+ // Filter by step-level allowed tools if specified
1620
+ if (step?.tools) {
1621
+ const allowedToolIds = new Set<string>();
1622
+ const stepTools: Tool<TContext, unknown[], unknown, unknown>[] = [];
1623
+
1624
+ for (const toolRef of step.tools) {
1625
+ if (typeof toolRef === "string") {
1626
+ // Reference to registered tool
1627
+ allowedToolIds.add(toolRef);
1628
+ } else {
1629
+ // Inline tool definition
1630
+ if (toolRef.id) {
1631
+ allowedToolIds.add(toolRef.id);
1632
+ stepTools.push(toolRef);
1633
+ }
1634
+ }
1635
+ }
1636
+
1637
+ // If step specifies tools, only include those
1638
+ if (allowedToolIds.size > 0) {
1639
+ const filteredTools = new Map<
1640
+ string,
1641
+ Tool<TContext, unknown[], unknown, unknown>
1642
+ >();
1643
+ for (const toolId of allowedToolIds) {
1644
+ const tool = availableTools.get(toolId);
1645
+ if (tool) {
1646
+ filteredTools.set(toolId, tool);
1647
+ }
1648
+ }
1649
+ // Add inline tools
1650
+ stepTools.forEach((tool) => {
1651
+ if (tool.id) {
1652
+ filteredTools.set(tool.id, tool);
1653
+ }
1654
+ });
1655
+ availableTools.clear();
1656
+ filteredTools.forEach((tool, id) => availableTools.set(id, tool));
1657
+ }
1658
+ }
1659
+
1660
+ // Convert to the format expected by AI providers
1661
+ return Array.from(availableTools.values()).map((tool) => ({
1662
+ id: tool.id,
1663
+ name: tool.name || tool.id,
1664
+ description: tool.description,
1665
+ parameters: tool.parameters,
1666
+ }));
1197
1667
  }
1198
1668
 
1199
1669
  /**
1200
- * Get the persistence manager (if configured)
1670
+ * Execute a prepare or finalize function/tool
1671
+ * @private
1201
1672
  */
1202
- getPersistenceManager(): PersistenceManager | undefined {
1203
- return this.persistenceManager;
1673
+ private async executePrepareFinalize(
1674
+ prepareOrFinalize:
1675
+ | string
1676
+ | Tool<TContext, unknown[], unknown, unknown>
1677
+ | ((context: TContext, data?: Partial<unknown>) => void | Promise<void>)
1678
+ | undefined,
1679
+ context: TContext,
1680
+ data?: Partial<unknown>,
1681
+ route?: Route<TContext, unknown>,
1682
+ step?: Step<TContext, unknown>
1683
+ ): Promise<void> {
1684
+ if (!prepareOrFinalize) return;
1685
+
1686
+ if (typeof prepareOrFinalize === "function") {
1687
+ // It's a function - call it directly
1688
+ await prepareOrFinalize(context, data);
1689
+ } else {
1690
+ // It's a tool reference - find and execute the tool
1691
+ let tool: Tool<TContext, unknown[], unknown, unknown> | undefined;
1692
+
1693
+ if (typeof prepareOrFinalize === "string") {
1694
+ // Tool ID - find it in available tools
1695
+ const availableTools = new Map<
1696
+ string,
1697
+ Tool<TContext, unknown[], unknown, unknown>
1698
+ >();
1699
+
1700
+ // Add agent-level tools
1701
+ this.tools.forEach((t) => {
1702
+ availableTools.set(t.id, t);
1703
+ });
1704
+
1705
+ // Add route-level tools
1706
+ if (route) {
1707
+ route.getTools().forEach((t) => {
1708
+ availableTools.set(t.id, t);
1709
+ });
1710
+ }
1711
+
1712
+ // Add step-level tools
1713
+ if (step?.tools) {
1714
+ for (const toolRef of step.tools) {
1715
+ if (typeof toolRef === "string") {
1716
+ // Keep as is
1717
+ } else if (toolRef.id) {
1718
+ availableTools.set(toolRef.id, toolRef);
1719
+ }
1720
+ }
1721
+ }
1722
+
1723
+ tool = availableTools.get(prepareOrFinalize);
1724
+ } else {
1725
+ // Tool object - use directly
1726
+ tool = prepareOrFinalize;
1727
+ }
1728
+
1729
+ if (tool) {
1730
+ const toolExecutor = new ToolExecutor<TContext, unknown>();
1731
+ const result = await toolExecutor.executeTool({
1732
+ tool,
1733
+ context,
1734
+ updateContext: this.updateContext.bind(this),
1735
+ history: [], // Empty history for prepare/finalize
1736
+ data,
1737
+ });
1738
+
1739
+ if (!result.success) {
1740
+ logger.error(
1741
+ `[Agent] Tool execution failed in prepare/finalize: ${result.error}`
1742
+ );
1743
+ throw new Error(`Tool execution failed: ${result.error}`);
1744
+ }
1745
+ } else {
1746
+ logger.warn(
1747
+ `[Agent] Tool not found for prepare/finalize: ${
1748
+ typeof prepareOrFinalize === "string"
1749
+ ? prepareOrFinalize
1750
+ : "inline tool"
1751
+ }`
1752
+ );
1753
+ }
1754
+ }
1204
1755
  }
1205
1756
 
1206
1757
  /**
1207
- * Check if persistence is enabled
1758
+ * Get all guidelines
1208
1759
  */
1209
- hasPersistence(): boolean {
1210
- return this.persistenceManager !== undefined;
1760
+ getGuidelines(): Guideline<TContext>[] {
1761
+ return [...this.guidelines];
1211
1762
  }
1212
1763
 
1213
1764
  /**
1214
- * Get allowed domains for a specific route
1215
- * @param routeId - Route ID to check
1216
- * @returns Filtered domains object, or all domains if route has no restrictions
1765
+ * Get the agent's knowledge base
1217
1766
  */
1218
- getDomainsForRoute(routeId: string): Record<string, Record<string, unknown>> {
1219
- const route = this.routes.find((r) => r.id === routeId);
1767
+ getKnowledgeBase(): Record<string, unknown> {
1768
+ return { ...this.knowledgeBase };
1769
+ }
1220
1770
 
1221
- if (!route) {
1222
- // Route not found, return all domains
1223
- return this.domainRegistry.all();
1224
- }
1771
+ /**
1772
+ * Merge terms with route-specific taking precedence on conflicts
1773
+ * @private
1774
+ */
1775
+ private mergeTerms(
1776
+ agentTerms: Term<TContext>[],
1777
+ routeTerms: Term<TContext>[]
1778
+ ): Term<TContext>[] {
1779
+ const merged = new Map<string, Term<TContext>>();
1780
+
1781
+ // Add agent terms first
1782
+ agentTerms.forEach((term) => {
1783
+ const name =
1784
+ typeof term.name === "string" ? term.name : term.name.toString();
1785
+ merged.set(name, term);
1786
+ });
1787
+
1788
+ // Add route terms (these take precedence)
1789
+ routeTerms.forEach((term) => {
1790
+ const name =
1791
+ typeof term.name === "string" ? term.name : term.name.toString();
1792
+ merged.set(name, term);
1793
+ });
1225
1794
 
1226
- const allowedDomains = route.getDomains();
1227
- return this.domainRegistry.getFiltered(allowedDomains);
1795
+ return Array.from(merged.values());
1228
1796
  }
1229
1797
 
1230
1798
  /**
1231
- * Get allowed domains for a specific route by title
1232
- * @param routeTitle - Route title to check
1233
- * @returns Filtered domains object, or all domains if route has no restrictions
1799
+ * Get the persistence manager (if configured)
1234
1800
  */
1235
- getDomainsForRouteByTitle(
1236
- routeTitle: string
1237
- ): Record<string, Record<string, unknown>> {
1238
- const route = this.routes.find((r) => r.title === routeTitle);
1239
-
1240
- if (!route) {
1241
- // Route not found, return all domains
1242
- return this.domainRegistry.all();
1243
- }
1801
+ getPersistenceManager(): PersistenceManager | undefined {
1802
+ return this.persistenceManager;
1803
+ }
1244
1804
 
1245
- const allowedDomains = route.getDomains();
1246
- return this.domainRegistry.getFiltered(allowedDomains);
1805
+ /**
1806
+ * Check if persistence is enabled
1807
+ */
1808
+ hasPersistence(): boolean {
1809
+ return this.persistenceManager !== undefined;
1247
1810
  }
1248
1811
 
1249
1812
  /**
@@ -1303,11 +1866,12 @@ export class Agent<TContext = unknown> {
1303
1866
  * const nextResponse = await agent.respond({ history, session: updatedSession });
1304
1867
  * }
1305
1868
  */
1306
- nextStepRoute(
1869
+ async nextStepRoute(
1307
1870
  routeIdOrTitle: string,
1308
1871
  session?: SessionState,
1309
- condition?: string
1310
- ): SessionState {
1872
+ condition?: Template<TContext, unknown>,
1873
+ history?: Event[]
1874
+ ): Promise<SessionState> {
1311
1875
  const targetSession = session || this.currentSession;
1312
1876
 
1313
1877
  if (!targetSession) {
@@ -1328,12 +1892,19 @@ export class Agent<TContext = unknown> {
1328
1892
  .join(", ")}`
1329
1893
  );
1330
1894
  }
1895
+ const templateContext = {
1896
+ context: this.context,
1897
+ session,
1898
+ history,
1899
+ data: this.currentSession?.data,
1900
+ };
1901
+ const renderedCondition = await render(condition, templateContext);
1331
1902
 
1332
1903
  const updatedSession: SessionState = {
1333
1904
  ...targetSession,
1334
1905
  pendingTransition: {
1335
1906
  targetRouteId: targetRoute.id,
1336
- condition,
1907
+ condition: renderedCondition,
1337
1908
  reason: "manual",
1338
1909
  },
1339
1910
  };
@@ -1349,4 +1920,48 @@ export class Agent<TContext = unknown> {
1349
1920
 
1350
1921
  return updatedSession;
1351
1922
  }
1923
+
1924
+ /**
1925
+ * Simplified respond method using SessionManager
1926
+ * Automatically manages conversation history through the session
1927
+ */
1928
+ async chat<TData = Record<string, unknown>>(
1929
+ message?: string,
1930
+ options?: {
1931
+ history?: History; // Optional: override session history for this response
1932
+ contextOverride?: Partial<TContext>;
1933
+ signal?: AbortSignal;
1934
+ }
1935
+ ): Promise<AgentResponse<TData>> {
1936
+ // Determine which history to use
1937
+ let history: History;
1938
+ if (options?.history) {
1939
+ // Use provided history for this response only
1940
+ history = options.history;
1941
+ } else {
1942
+ // Add user message to session history if provided
1943
+ if (message) {
1944
+ await this.session.addMessage("user", message);
1945
+ }
1946
+ history = this.session.getHistory();
1947
+ }
1948
+
1949
+ // Get or create session
1950
+ const session = await this.session.getOrCreate();
1951
+
1952
+ // Use existing respond method with session-managed history
1953
+ const result = await this.respond({
1954
+ history,
1955
+ session,
1956
+ contextOverride: options?.contextOverride,
1957
+ signal: options?.signal,
1958
+ });
1959
+
1960
+ // Add agent response to session history (only if not using override history)
1961
+ if (!options?.history) {
1962
+ await this.session.addMessage("assistant", result.message);
1963
+ }
1964
+
1965
+ return result as AgentResponse<TData>;
1966
+ }
1352
1967
  }