@multiplayer-app/ai-agent-node 0.1.0-beta.8 → 0.1.0-beta.81

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 (338) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/config.cjs +88 -37
  3. package/dist/cjs/config.cjs.map +1 -1
  4. package/dist/cjs/config.d.ts +62 -23
  5. package/dist/cjs/config.d.ts.map +1 -1
  6. package/dist/cjs/helpers/AIHelper.cjs +134 -68
  7. package/dist/cjs/helpers/AIHelper.cjs.map +1 -1
  8. package/dist/cjs/helpers/AIHelper.d.ts +24 -16
  9. package/dist/cjs/helpers/AIHelper.d.ts.map +1 -1
  10. package/dist/cjs/helpers/AIHelper.test.cjs +22 -15
  11. package/dist/cjs/helpers/AIHelper.test.cjs.map +1 -1
  12. package/dist/cjs/helpers/ConfigHelper.cjs +15 -6
  13. package/dist/cjs/helpers/ConfigHelper.cjs.map +1 -1
  14. package/dist/cjs/helpers/ConfigHelper.d.ts.map +1 -1
  15. package/dist/cjs/helpers/FileHelper.cjs +131 -151
  16. package/dist/cjs/helpers/FileHelper.cjs.map +1 -1
  17. package/dist/cjs/helpers/FileHelper.d.ts +19 -25
  18. package/dist/cjs/helpers/FileHelper.d.ts.map +1 -1
  19. package/dist/cjs/helpers/index.cjs +0 -1
  20. package/dist/cjs/helpers/index.cjs.map +1 -1
  21. package/dist/cjs/helpers/index.d.ts +0 -1
  22. package/dist/cjs/helpers/index.d.ts.map +1 -1
  23. package/dist/cjs/index.cjs +125 -28
  24. package/dist/cjs/index.cjs.map +1 -1
  25. package/dist/cjs/index.d.ts +47 -11
  26. package/dist/cjs/index.d.ts.map +1 -1
  27. package/dist/cjs/libs/index.cjs +0 -1
  28. package/dist/cjs/libs/index.cjs.map +1 -1
  29. package/dist/cjs/libs/index.d.ts +0 -1
  30. package/dist/cjs/libs/index.d.ts.map +1 -1
  31. package/dist/cjs/libs/s3/index.cjs +3 -39
  32. package/dist/cjs/libs/s3/index.cjs.map +1 -1
  33. package/dist/cjs/libs/s3/index.d.ts +1 -2
  34. package/dist/cjs/libs/s3/index.d.ts.map +1 -1
  35. package/dist/cjs/libs/s3/s3.lib.cjs +173 -186
  36. package/dist/cjs/libs/s3/s3.lib.cjs.map +1 -1
  37. package/dist/cjs/libs/s3/s3.lib.d.ts +29 -22
  38. package/dist/cjs/libs/s3/s3.lib.d.ts.map +1 -1
  39. package/dist/cjs/processors/ActivityProcessor.cjs +53 -0
  40. package/dist/cjs/processors/ActivityProcessor.cjs.map +1 -0
  41. package/dist/cjs/processors/ActivityProcessor.d.ts +34 -0
  42. package/dist/cjs/processors/ActivityProcessor.d.ts.map +1 -0
  43. package/dist/cjs/processors/ActivityProcessor.test.cjs +139 -0
  44. package/dist/cjs/processors/ActivityProcessor.test.cjs.map +1 -0
  45. package/dist/cjs/processors/ActivityProcessor.test.d.ts +2 -0
  46. package/dist/cjs/processors/ActivityProcessor.test.d.ts.map +1 -0
  47. package/dist/cjs/processors/AgentProcessor.cjs +47 -0
  48. package/dist/cjs/processors/AgentProcessor.cjs.map +1 -0
  49. package/dist/cjs/processors/AgentProcessor.d.ts +25 -0
  50. package/dist/cjs/processors/AgentProcessor.d.ts.map +1 -0
  51. package/dist/cjs/processors/AgentProcessor.test.cjs +103 -0
  52. package/dist/cjs/processors/AgentProcessor.test.cjs.map +1 -0
  53. package/dist/cjs/processors/AgentProcessor.test.d.ts +2 -0
  54. package/dist/cjs/processors/AgentProcessor.test.d.ts.map +1 -0
  55. package/dist/cjs/processors/ChatProcessor.cjs +1029 -148
  56. package/dist/cjs/processors/ChatProcessor.cjs.map +1 -1
  57. package/dist/cjs/processors/ChatProcessor.d.ts +108 -12
  58. package/dist/cjs/processors/ChatProcessor.d.ts.map +1 -1
  59. package/dist/cjs/processors/ChatProcessor.test.cjs +803 -0
  60. package/dist/cjs/processors/ChatProcessor.test.cjs.map +1 -0
  61. package/dist/cjs/processors/ChatProcessor.test.d.ts +2 -0
  62. package/dist/cjs/processors/ChatProcessor.test.d.ts.map +1 -0
  63. package/dist/cjs/processors/index.cjs +2 -0
  64. package/dist/cjs/processors/index.cjs.map +1 -1
  65. package/dist/cjs/processors/index.d.ts +2 -0
  66. package/dist/cjs/processors/index.d.ts.map +1 -1
  67. package/dist/cjs/services/AIService.cjs +114 -68
  68. package/dist/cjs/services/AIService.cjs.map +1 -1
  69. package/dist/cjs/services/AIService.d.ts +34 -13
  70. package/dist/cjs/services/AIService.d.ts.map +1 -1
  71. package/dist/cjs/services/CredentialProvider.cjs +62 -0
  72. package/dist/cjs/services/CredentialProvider.cjs.map +1 -0
  73. package/dist/cjs/services/CredentialProvider.d.ts +20 -0
  74. package/dist/cjs/services/CredentialProvider.d.ts.map +1 -0
  75. package/dist/cjs/services/CredentialProvider.test.cjs +71 -0
  76. package/dist/cjs/services/CredentialProvider.test.cjs.map +1 -0
  77. package/dist/cjs/services/CredentialProvider.test.d.ts +2 -0
  78. package/dist/cjs/services/CredentialProvider.test.d.ts.map +1 -0
  79. package/dist/cjs/services/ExecutionContext.cjs +3 -0
  80. package/dist/cjs/services/ExecutionContext.cjs.map +1 -0
  81. package/dist/cjs/services/ExecutionContext.d.ts +4 -0
  82. package/dist/cjs/services/ExecutionContext.d.ts.map +1 -0
  83. package/dist/cjs/services/InternalEventsHandler.cjs +3 -3
  84. package/dist/cjs/services/InternalEventsHandler.cjs.map +1 -1
  85. package/dist/cjs/services/InternalEventsHandler.d.ts +3 -1
  86. package/dist/cjs/services/InternalEventsHandler.d.ts.map +1 -1
  87. package/dist/cjs/services/ModelAccessPolicy.cjs +24 -0
  88. package/dist/cjs/services/ModelAccessPolicy.cjs.map +1 -0
  89. package/dist/cjs/services/ModelAccessPolicy.d.ts +7 -0
  90. package/dist/cjs/services/ModelAccessPolicy.d.ts.map +1 -0
  91. package/dist/cjs/services/ModelFetcher.cjs +2 -8
  92. package/dist/cjs/services/ModelFetcher.cjs.map +1 -1
  93. package/dist/cjs/services/ModelFetcher.d.ts +2 -7
  94. package/dist/cjs/services/ModelFetcher.d.ts.map +1 -1
  95. package/dist/cjs/services/ProviderClientFactory.cjs +29 -0
  96. package/dist/cjs/services/ProviderClientFactory.cjs.map +1 -0
  97. package/dist/cjs/services/ProviderClientFactory.d.ts +9 -0
  98. package/dist/cjs/services/ProviderClientFactory.d.ts.map +1 -0
  99. package/dist/cjs/services/RedisService.cjs +20 -16
  100. package/dist/cjs/services/RedisService.cjs.map +1 -1
  101. package/dist/cjs/services/RedisService.d.ts +5 -2
  102. package/dist/cjs/services/RedisService.d.ts.map +1 -1
  103. package/dist/cjs/services/SocketService.cjs +8 -8
  104. package/dist/cjs/services/SocketService.cjs.map +1 -1
  105. package/dist/cjs/services/SocketService.d.ts +9 -6
  106. package/dist/cjs/services/SocketService.d.ts.map +1 -1
  107. package/dist/cjs/services/TenantCredentialResolver.cjs +136 -0
  108. package/dist/cjs/services/TenantCredentialResolver.cjs.map +1 -0
  109. package/dist/cjs/services/TenantCredentialResolver.d.ts +32 -0
  110. package/dist/cjs/services/TenantCredentialResolver.d.ts.map +1 -0
  111. package/dist/cjs/services/TenantCredentialResolver.test.cjs +113 -0
  112. package/dist/cjs/services/TenantCredentialResolver.test.cjs.map +1 -0
  113. package/dist/cjs/services/TenantCredentialResolver.test.d.ts +2 -0
  114. package/dist/cjs/services/TenantCredentialResolver.test.d.ts.map +1 -0
  115. package/dist/cjs/services/index.cjs +5 -1
  116. package/dist/cjs/services/index.cjs.map +1 -1
  117. package/dist/cjs/services/index.d.ts +5 -1
  118. package/dist/cjs/services/index.d.ts.map +1 -1
  119. package/dist/cjs/store/AgentStore.cjs +14 -4
  120. package/dist/cjs/store/AgentStore.cjs.map +1 -1
  121. package/dist/cjs/store/AgentStore.d.ts +3 -1
  122. package/dist/cjs/store/AgentStore.d.ts.map +1 -1
  123. package/dist/cjs/store/ConfigStore.cjs +14 -3
  124. package/dist/cjs/store/ConfigStore.cjs.map +1 -1
  125. package/dist/cjs/store/ConfigStore.d.ts +2 -0
  126. package/dist/cjs/store/ConfigStore.d.ts.map +1 -1
  127. package/dist/cjs/tools/generateChartTool.d.ts +2 -2
  128. package/dist/cjs/tools/proposeFormValuesTool.d.ts +2 -2
  129. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  130. package/dist/cjs/utils/utils.cjs +31 -0
  131. package/dist/cjs/utils/utils.cjs.map +1 -0
  132. package/dist/cjs/utils/utils.d.ts +5 -0
  133. package/dist/cjs/utils/utils.d.ts.map +1 -0
  134. package/dist/esm/config.d.ts +62 -23
  135. package/dist/esm/config.d.ts.map +1 -1
  136. package/dist/esm/config.js +88 -35
  137. package/dist/esm/config.js.map +1 -1
  138. package/dist/esm/helpers/AIHelper.d.ts +24 -16
  139. package/dist/esm/helpers/AIHelper.d.ts.map +1 -1
  140. package/dist/esm/helpers/AIHelper.js +141 -73
  141. package/dist/esm/helpers/AIHelper.js.map +1 -1
  142. package/dist/esm/helpers/AIHelper.test.js +22 -15
  143. package/dist/esm/helpers/AIHelper.test.js.map +1 -1
  144. package/dist/esm/helpers/ConfigHelper.d.ts.map +1 -1
  145. package/dist/esm/helpers/ConfigHelper.js +15 -6
  146. package/dist/esm/helpers/ConfigHelper.js.map +1 -1
  147. package/dist/esm/helpers/FileHelper.d.ts +19 -25
  148. package/dist/esm/helpers/FileHelper.d.ts.map +1 -1
  149. package/dist/esm/helpers/FileHelper.js +131 -146
  150. package/dist/esm/helpers/FileHelper.js.map +1 -1
  151. package/dist/esm/helpers/index.d.ts +0 -1
  152. package/dist/esm/helpers/index.d.ts.map +1 -1
  153. package/dist/esm/helpers/index.js +0 -1
  154. package/dist/esm/helpers/index.js.map +1 -1
  155. package/dist/esm/index.d.ts +47 -11
  156. package/dist/esm/index.d.ts.map +1 -1
  157. package/dist/esm/index.js +98 -11
  158. package/dist/esm/index.js.map +1 -1
  159. package/dist/esm/libs/index.d.ts +0 -1
  160. package/dist/esm/libs/index.d.ts.map +1 -1
  161. package/dist/esm/libs/index.js +0 -1
  162. package/dist/esm/libs/index.js.map +1 -1
  163. package/dist/esm/libs/s3/index.d.ts +1 -2
  164. package/dist/esm/libs/s3/index.d.ts.map +1 -1
  165. package/dist/esm/libs/s3/index.js +1 -2
  166. package/dist/esm/libs/s3/index.js.map +1 -1
  167. package/dist/esm/libs/s3/s3.lib.d.ts +29 -22
  168. package/dist/esm/libs/s3/s3.lib.d.ts.map +1 -1
  169. package/dist/esm/libs/s3/s3.lib.js +177 -172
  170. package/dist/esm/libs/s3/s3.lib.js.map +1 -1
  171. package/dist/esm/processors/ActivityProcessor.d.ts +34 -0
  172. package/dist/esm/processors/ActivityProcessor.d.ts.map +1 -0
  173. package/dist/esm/processors/ActivityProcessor.js +50 -0
  174. package/dist/esm/processors/ActivityProcessor.js.map +1 -0
  175. package/dist/esm/processors/ActivityProcessor.test.d.ts +2 -0
  176. package/dist/esm/processors/ActivityProcessor.test.d.ts.map +1 -0
  177. package/dist/esm/processors/ActivityProcessor.test.js +137 -0
  178. package/dist/esm/processors/ActivityProcessor.test.js.map +1 -0
  179. package/dist/esm/processors/AgentProcessor.d.ts +25 -0
  180. package/dist/esm/processors/AgentProcessor.d.ts.map +1 -0
  181. package/dist/esm/processors/AgentProcessor.js +44 -0
  182. package/dist/esm/processors/AgentProcessor.js.map +1 -0
  183. package/dist/esm/processors/AgentProcessor.test.d.ts +2 -0
  184. package/dist/esm/processors/AgentProcessor.test.d.ts.map +1 -0
  185. package/dist/esm/processors/AgentProcessor.test.js +101 -0
  186. package/dist/esm/processors/AgentProcessor.test.js.map +1 -0
  187. package/dist/esm/processors/ChatProcessor.d.ts +108 -12
  188. package/dist/esm/processors/ChatProcessor.d.ts.map +1 -1
  189. package/dist/esm/processors/ChatProcessor.js +1038 -150
  190. package/dist/esm/processors/ChatProcessor.js.map +1 -1
  191. package/dist/esm/processors/ChatProcessor.test.d.ts +2 -0
  192. package/dist/esm/processors/ChatProcessor.test.d.ts.map +1 -0
  193. package/dist/esm/processors/ChatProcessor.test.js +801 -0
  194. package/dist/esm/processors/ChatProcessor.test.js.map +1 -0
  195. package/dist/esm/processors/index.d.ts +2 -0
  196. package/dist/esm/processors/index.d.ts.map +1 -1
  197. package/dist/esm/processors/index.js +2 -0
  198. package/dist/esm/processors/index.js.map +1 -1
  199. package/dist/esm/services/AIService.d.ts +34 -13
  200. package/dist/esm/services/AIService.d.ts.map +1 -1
  201. package/dist/esm/services/AIService.js +118 -68
  202. package/dist/esm/services/AIService.js.map +1 -1
  203. package/dist/esm/services/CredentialProvider.d.ts +20 -0
  204. package/dist/esm/services/CredentialProvider.d.ts.map +1 -0
  205. package/dist/esm/services/CredentialProvider.js +60 -0
  206. package/dist/esm/services/CredentialProvider.js.map +1 -0
  207. package/dist/esm/services/CredentialProvider.test.d.ts +2 -0
  208. package/dist/esm/services/CredentialProvider.test.d.ts.map +1 -0
  209. package/dist/esm/services/CredentialProvider.test.js +69 -0
  210. package/dist/esm/services/CredentialProvider.test.js.map +1 -0
  211. package/dist/esm/services/ExecutionContext.d.ts +4 -0
  212. package/dist/esm/services/ExecutionContext.d.ts.map +1 -0
  213. package/dist/esm/services/ExecutionContext.js +2 -0
  214. package/dist/esm/services/ExecutionContext.js.map +1 -0
  215. package/dist/esm/services/InternalEventsHandler.d.ts +3 -1
  216. package/dist/esm/services/InternalEventsHandler.d.ts.map +1 -1
  217. package/dist/esm/services/InternalEventsHandler.js +4 -3
  218. package/dist/esm/services/InternalEventsHandler.js.map +1 -1
  219. package/dist/esm/services/ModelAccessPolicy.d.ts +7 -0
  220. package/dist/esm/services/ModelAccessPolicy.d.ts.map +1 -0
  221. package/dist/esm/services/ModelAccessPolicy.js +20 -0
  222. package/dist/esm/services/ModelAccessPolicy.js.map +1 -0
  223. package/dist/esm/services/ModelFetcher.d.ts +2 -7
  224. package/dist/esm/services/ModelFetcher.d.ts.map +1 -1
  225. package/dist/esm/services/ModelFetcher.js +2 -8
  226. package/dist/esm/services/ModelFetcher.js.map +1 -1
  227. package/dist/esm/services/ProviderClientFactory.d.ts +9 -0
  228. package/dist/esm/services/ProviderClientFactory.d.ts.map +1 -0
  229. package/dist/esm/services/ProviderClientFactory.js +25 -0
  230. package/dist/esm/services/ProviderClientFactory.js.map +1 -0
  231. package/dist/esm/services/RedisService.d.ts +5 -2
  232. package/dist/esm/services/RedisService.d.ts.map +1 -1
  233. package/dist/esm/services/RedisService.js +21 -14
  234. package/dist/esm/services/RedisService.js.map +1 -1
  235. package/dist/esm/services/SocketService.d.ts +9 -6
  236. package/dist/esm/services/SocketService.d.ts.map +1 -1
  237. package/dist/esm/services/SocketService.js +10 -6
  238. package/dist/esm/services/SocketService.js.map +1 -1
  239. package/dist/esm/services/TenantCredentialResolver.d.ts +32 -0
  240. package/dist/esm/services/TenantCredentialResolver.d.ts.map +1 -0
  241. package/dist/esm/services/TenantCredentialResolver.js +133 -0
  242. package/dist/esm/services/TenantCredentialResolver.js.map +1 -0
  243. package/dist/esm/services/TenantCredentialResolver.test.d.ts +2 -0
  244. package/dist/esm/services/TenantCredentialResolver.test.d.ts.map +1 -0
  245. package/dist/esm/services/TenantCredentialResolver.test.js +111 -0
  246. package/dist/esm/services/TenantCredentialResolver.test.js.map +1 -0
  247. package/dist/esm/services/index.d.ts +5 -1
  248. package/dist/esm/services/index.d.ts.map +1 -1
  249. package/dist/esm/services/index.js +5 -1
  250. package/dist/esm/services/index.js.map +1 -1
  251. package/dist/esm/store/AgentStore.d.ts +3 -1
  252. package/dist/esm/store/AgentStore.d.ts.map +1 -1
  253. package/dist/esm/store/AgentStore.js +15 -2
  254. package/dist/esm/store/AgentStore.js.map +1 -1
  255. package/dist/esm/store/ConfigStore.d.ts +2 -0
  256. package/dist/esm/store/ConfigStore.d.ts.map +1 -1
  257. package/dist/esm/store/ConfigStore.js +14 -3
  258. package/dist/esm/store/ConfigStore.js.map +1 -1
  259. package/dist/esm/tools/generateChartTool.d.ts +2 -2
  260. package/dist/esm/tools/proposeFormValuesTool.d.ts +2 -2
  261. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  262. package/dist/esm/utils/utils.d.ts +5 -0
  263. package/dist/esm/utils/utils.d.ts.map +1 -0
  264. package/dist/esm/utils/utils.js +26 -0
  265. package/dist/esm/utils/utils.js.map +1 -0
  266. package/package.json +7 -8
  267. package/dist/cjs/helpers/SetupHelper.cjs +0 -37
  268. package/dist/cjs/helpers/SetupHelper.cjs.map +0 -1
  269. package/dist/cjs/helpers/SetupHelper.d.ts +0 -5
  270. package/dist/cjs/helpers/SetupHelper.d.ts.map +0 -1
  271. package/dist/cjs/libs/kafka/config.cjs +0 -8
  272. package/dist/cjs/libs/kafka/config.cjs.map +0 -1
  273. package/dist/cjs/libs/kafka/config.d.ts +0 -5
  274. package/dist/cjs/libs/kafka/config.d.ts.map +0 -1
  275. package/dist/cjs/libs/kafka/consumer.cjs +0 -131
  276. package/dist/cjs/libs/kafka/consumer.cjs.map +0 -1
  277. package/dist/cjs/libs/kafka/consumer.d.ts +0 -16
  278. package/dist/cjs/libs/kafka/consumer.d.ts.map +0 -1
  279. package/dist/cjs/libs/kafka/index.cjs +0 -19
  280. package/dist/cjs/libs/kafka/index.cjs.map +0 -1
  281. package/dist/cjs/libs/kafka/index.d.ts +0 -3
  282. package/dist/cjs/libs/kafka/index.d.ts.map +0 -1
  283. package/dist/cjs/libs/kafka/kafka.cjs +0 -27
  284. package/dist/cjs/libs/kafka/kafka.cjs.map +0 -1
  285. package/dist/cjs/libs/kafka/kafka.d.ts +0 -3
  286. package/dist/cjs/libs/kafka/kafka.d.ts.map +0 -1
  287. package/dist/cjs/libs/kafka/producer.cjs +0 -48
  288. package/dist/cjs/libs/kafka/producer.cjs.map +0 -1
  289. package/dist/cjs/libs/kafka/producer.d.ts +0 -11
  290. package/dist/cjs/libs/kafka/producer.d.ts.map +0 -1
  291. package/dist/cjs/libs/logger/config.cjs +0 -8
  292. package/dist/cjs/libs/logger/config.cjs.map +0 -1
  293. package/dist/cjs/libs/logger/config.d.ts +0 -5
  294. package/dist/cjs/libs/logger/config.d.ts.map +0 -1
  295. package/dist/cjs/libs/s3/config.cjs +0 -10
  296. package/dist/cjs/libs/s3/config.cjs.map +0 -1
  297. package/dist/cjs/libs/s3/config.d.ts +0 -7
  298. package/dist/cjs/libs/s3/config.d.ts.map +0 -1
  299. package/dist/cjs/services/KafkaService.cjs +0 -122
  300. package/dist/cjs/services/KafkaService.cjs.map +0 -1
  301. package/dist/cjs/services/KafkaService.d.ts +0 -35
  302. package/dist/cjs/services/KafkaService.d.ts.map +0 -1
  303. package/dist/esm/helpers/SetupHelper.d.ts +0 -5
  304. package/dist/esm/helpers/SetupHelper.d.ts.map +0 -1
  305. package/dist/esm/helpers/SetupHelper.js +0 -32
  306. package/dist/esm/helpers/SetupHelper.js.map +0 -1
  307. package/dist/esm/libs/kafka/config.d.ts +0 -5
  308. package/dist/esm/libs/kafka/config.d.ts.map +0 -1
  309. package/dist/esm/libs/kafka/config.js +0 -5
  310. package/dist/esm/libs/kafka/config.js.map +0 -1
  311. package/dist/esm/libs/kafka/consumer.d.ts +0 -16
  312. package/dist/esm/libs/kafka/consumer.d.ts.map +0 -1
  313. package/dist/esm/libs/kafka/consumer.js +0 -125
  314. package/dist/esm/libs/kafka/consumer.js.map +0 -1
  315. package/dist/esm/libs/kafka/index.d.ts +0 -3
  316. package/dist/esm/libs/kafka/index.d.ts.map +0 -1
  317. package/dist/esm/libs/kafka/index.js +0 -3
  318. package/dist/esm/libs/kafka/index.js.map +0 -1
  319. package/dist/esm/libs/kafka/kafka.d.ts +0 -3
  320. package/dist/esm/libs/kafka/kafka.d.ts.map +0 -1
  321. package/dist/esm/libs/kafka/kafka.js +0 -24
  322. package/dist/esm/libs/kafka/kafka.js.map +0 -1
  323. package/dist/esm/libs/kafka/producer.d.ts +0 -11
  324. package/dist/esm/libs/kafka/producer.d.ts.map +0 -1
  325. package/dist/esm/libs/kafka/producer.js +0 -45
  326. package/dist/esm/libs/kafka/producer.js.map +0 -1
  327. package/dist/esm/libs/logger/config.d.ts +0 -5
  328. package/dist/esm/libs/logger/config.d.ts.map +0 -1
  329. package/dist/esm/libs/logger/config.js +0 -5
  330. package/dist/esm/libs/logger/config.js.map +0 -1
  331. package/dist/esm/libs/s3/config.d.ts +0 -7
  332. package/dist/esm/libs/s3/config.d.ts.map +0 -1
  333. package/dist/esm/libs/s3/config.js +0 -7
  334. package/dist/esm/libs/s3/config.js.map +0 -1
  335. package/dist/esm/services/KafkaService.d.ts +0 -35
  336. package/dist/esm/services/KafkaService.d.ts.map +0 -1
  337. package/dist/esm/services/KafkaService.js +0 -123
  338. package/dist/esm/services/KafkaService.js.map +0 -1
@@ -1,76 +1,369 @@
1
- import { AgentStatus, AgentToolCallStatus, ChatType, MessageRole, SortOrder, StreamChunkType } from '@multiplayer-app/ai-agent-types';
2
- import { socketService } from '../services';
3
- import { kafkaService } from '../services';
4
- import { AIHelper } from '../helpers';
5
- import { AgentProcessEventType, agentStore } from '../store';
6
- import { ContextLimiter } from '../helpers';
7
- import { config } from '../config';
8
- import { PassThrough, pipeline } from 'stream';
9
- import { promisify } from 'util';
1
+ import { ActivityOperationName, AgentStatus, AgentToolCallStatus, AgentToolType, ChatType, MessageRole, SortOrder, StreamChunkType } from '@multiplayer-app/ai-agent-types';
2
+ import { z } from 'zod';
3
+ import { AgentProcessEventType, ConfigStore } from '../store';
4
+ import { AIHelper, ContextLimiter, FileHelper } from '../helpers';
5
+ import { PassThrough } from 'stream';
10
6
  import { logger } from '../libs/logger';
11
- const pipelineAsync = promisify(pipeline);
7
+ import { AgentSessionKind } from "@multiplayer-app/ai-agent-types";
8
+ import { getAgentToolName } from "../utils/utils";
12
9
  export class ChatProcessor {
10
+ aiHelper;
13
11
  chatRepository;
14
12
  messageRepository;
15
13
  artifactStore;
16
- constructor(chatRepository, messageRepository, artifactStore) {
17
- this.chatRepository = chatRepository;
18
- this.messageRepository = messageRepository;
19
- this.artifactStore = artifactStore;
14
+ activityRepository;
15
+ agentConfigRepository;
16
+ config;
17
+ socketService;
18
+ agentStore;
19
+ debug;
20
+ getChatUserId(chat) {
21
+ return chat.tenants?.userId;
22
+ }
23
+ constructor(params) {
24
+ this.chatRepository = params.chatRepository;
25
+ this.messageRepository = params.messageRepository;
26
+ this.artifactStore = params.artifactStore;
27
+ this.config = params.config;
28
+ this.socketService = params.socketService;
29
+ this.agentStore = params.agentStore;
30
+ this.activityRepository = params.activityRepository;
31
+ this.agentConfigRepository = params.agentConfigRepository;
32
+ const fileHelper = new FileHelper(params.s3Lib, this.config);
33
+ this.aiHelper = new AIHelper(fileHelper, this.config, params.tenantCredentialResolver);
34
+ this.debug = this.config.debug;
20
35
  }
21
36
  getTemporaryTitle(contextKey) {
22
37
  return `${contextKey} session ${new Date().toISOString()}`;
23
38
  }
39
+ isTemporaryTitle(title) {
40
+ // Matches pattern: "{contextKey} session {ISO_DATE_STRING}"
41
+ // Example: "default session 2026-01-23T10:30:45.123Z"
42
+ // toISOString() always includes milliseconds, so we require them
43
+ const pattern = /^.+ session \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
44
+ return pattern.test(title);
45
+ }
46
+ isPlainObject(value) {
47
+ return !!value && typeof value === 'object' && !Array.isArray(value);
48
+ }
49
+ getExecutionTenants(payload) {
50
+ const contextTenants = payload.executionContext?.tenants;
51
+ if (contextTenants && Object.keys(contextTenants).length > 0) {
52
+ return contextTenants;
53
+ }
54
+ return payload.tenants ?? {};
55
+ }
56
+ getParticipantIds(chat) {
57
+ const primary = chat.userId ?? this.getChatUserId(chat);
58
+ return Array.from(new Set([primary, ...(chat.participantIds ?? [])].filter(Boolean)));
59
+ }
60
+ hasChatAccess(chat, userId) {
61
+ if (!userId)
62
+ return true;
63
+ return this.getParticipantIds(chat).includes(userId);
64
+ }
65
+ emitChatToParticipants(chat, chatToEmit, excludeSocketId) {
66
+ for (const userId of this.getParticipantIds(chatToEmit.participantIds ? chatToEmit : chat)) {
67
+ this.socketService.emitChatUpdate(userId, chatToEmit, excludeSocketId);
68
+ }
69
+ }
70
+ emitMessageToParticipants(chat, message, excludeSocketId) {
71
+ for (const userId of this.getParticipantIds(chat)) {
72
+ this.socketService.emitMessageUpdate(userId, message, excludeSocketId);
73
+ }
74
+ }
75
+ async updateChatAndEmit(chat, update, excludeSocketId) {
76
+ await this.chatRepository.update(chat.id, update);
77
+ const updatedChat = await this.chatRepository.findById(chat.id);
78
+ const chatToEmit = updatedChat ?? { ...chat, ...update };
79
+ this.emitChatToParticipants(chat, chatToEmit, excludeSocketId);
80
+ return chatToEmit;
81
+ }
82
+ async startSubagentProcess(params) {
83
+ const parentUserId = params.parentChat.userId ?? this.getChatUserId(params.parentChat) ?? 'guest';
84
+ const childChat = await this.chatRepository.create({
85
+ title: `${params.subAgentConfig.name} subagent`,
86
+ type: ChatType.Agent,
87
+ status: AgentStatus.Streaming,
88
+ sessionKind: AgentSessionKind.SUBAGENT,
89
+ parentChatId: params.parentChat.id,
90
+ parentMessageId: params.parentMessage.id,
91
+ parentToolCallId: params.parentToolCallId,
92
+ contextKey: params.parentChat.contextKey,
93
+ userId: parentUserId,
94
+ tenants: { ...params.parentChat.tenants },
95
+ model: params.subAgentConfig.defaultModel || params.parentChat.model,
96
+ });
97
+ try {
98
+ const parentToolCall = params.parentMessage.toolCalls?.find((toolCall) => toolCall.id === params.parentToolCallId);
99
+ if (parentToolCall) {
100
+ parentToolCall.input = {
101
+ ...(parentToolCall.input ?? {}),
102
+ subagentChatId: childChat.id,
103
+ };
104
+ parentToolCall.subagentChatId = childChat.id;
105
+ const updatedParentMessage = await this.messageRepository.update(params.parentMessage.id, {
106
+ toolCalls: params.parentMessage.toolCalls,
107
+ });
108
+ if (updatedParentMessage) {
109
+ this.emitMessageToParticipants(params.parentChat, updatedParentMessage);
110
+ }
111
+ }
112
+ this.emitChatToParticipants(params.parentChat, childChat);
113
+ const executionTenants = this.getExecutionTenants(params);
114
+ const abortController = this.agentStore.registerSubAgentProcess(childChat.id, params.parentChat.id);
115
+ const userMessage = await this.createMessage(childChat, MessageRole.User, {
116
+ content: JSON.stringify(params.input),
117
+ agentName: undefined
118
+ });
119
+ await this.agentStore.shareAgentProcessEvent(childChat.id, {
120
+ type: AgentProcessEventType.Update,
121
+ data: userMessage
122
+ });
123
+ let assistantMessage = await this.createMessage(childChat, MessageRole.Assistant, {
124
+ content: '',
125
+ agentName: params.subAgentConfig.name
126
+ });
127
+ const parentActivity = await this.activityRepository.create({
128
+ ownerId: parentUserId,
129
+ groupId: params.parentChat.id,
130
+ name: ActivityOperationName.SUBAGENT,
131
+ tenants: executionTenants,
132
+ sourceId: assistantMessage.id,
133
+ sourceType: 'AgentMessage',
134
+ metadata: {
135
+ modelId: this.aiHelper.getLanguageModelId(childChat.model),
136
+ agentOptions: {
137
+ name: params.subAgentConfig.name,
138
+ modelId: this.aiHelper.getLanguageModelId(params.subAgentConfig.defaultModel),
139
+ temperature: params.subAgentConfig.temperature,
140
+ maxOutputTokens: params.subAgentConfig.maxOutputTokens,
141
+ topP: params.subAgentConfig.topP,
142
+ topK: params.subAgentConfig.topK,
143
+ presencePenalty: params.subAgentConfig.presencePenalty,
144
+ frequencyPenalty: params.subAgentConfig.frequencyPenalty,
145
+ stopSequences: params.subAgentConfig.stopSequences,
146
+ seed: params.subAgentConfig.seed,
147
+ },
148
+ },
149
+ });
150
+ const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
151
+ activity: parentActivity.id
152
+ });
153
+ const agentOptions = this.aiHelper.getAgentOptionsFromConfig(params.subAgentConfig, params.context);
154
+ agentOptions.onStepFinish = (stepResult) => {
155
+ return this.storeStepActivity({
156
+ chat: params.parentChat,
157
+ parentId: assistantMessage.activity,
158
+ stepResult,
159
+ name: ActivityOperationName.STEP_FINISHED,
160
+ sourceId: assistantMessage.id,
161
+ sourceType: 'AgentMessage',
162
+ tenants: executionTenants,
163
+ });
164
+ };
165
+ const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(parentUserId, agentOptions.name);
166
+ agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
167
+ await this.streamMessageStep({
168
+ chat: childChat,
169
+ existingMessages: [userMessage, updatedAssistantMessage],
170
+ assistantMessage: updatedAssistantMessage,
171
+ agentOptions,
172
+ signal: abortController.signal,
173
+ tenants: executionTenants,
174
+ executionContext: params.executionContext,
175
+ context: params.context,
176
+ });
177
+ }
178
+ catch (error) {
179
+ await this.storeSubagentResponse({
180
+ subagentChat: childChat,
181
+ content: undefined,
182
+ error: error,
183
+ context: params.context,
184
+ executionContext: params.executionContext,
185
+ });
186
+ }
187
+ }
188
+ mergeMissingUsageFields(target, source) {
189
+ for (const [key, sourceValue] of Object.entries(source)) {
190
+ const targetValue = target[key];
191
+ if (targetValue === undefined) {
192
+ target[key] = sourceValue;
193
+ continue;
194
+ }
195
+ if (this.isPlainObject(targetValue) && this.isPlainObject(sourceValue)) {
196
+ this.mergeMissingUsageFields(targetValue, sourceValue);
197
+ }
198
+ }
199
+ }
200
+ extractCostUsageFields(source) {
201
+ const extracted = {};
202
+ for (const [key, value] of Object.entries(source)) {
203
+ const normalizedKey = key.toLowerCase();
204
+ if (normalizedKey.includes('cost')) {
205
+ extracted[key] = value;
206
+ continue;
207
+ }
208
+ if (this.isPlainObject(value)) {
209
+ const nestedCostFields = this.extractCostUsageFields(value);
210
+ if (Object.keys(nestedCostFields).length > 0) {
211
+ extracted[key] = nestedCostFields;
212
+ }
213
+ }
214
+ }
215
+ return extracted;
216
+ }
217
+ mergeUsageWithProviderMetadata(stepUsage, providerMetadata) {
218
+ const mergedUsage = this.isPlainObject(stepUsage) ? { ...stepUsage } : {};
219
+ let hasProviderUsage = false;
220
+ if (this.isPlainObject(providerMetadata)) {
221
+ for (const providerEntry of Object.values(providerMetadata)) {
222
+ if (!this.isPlainObject(providerEntry) || !this.isPlainObject(providerEntry.usage)) {
223
+ continue;
224
+ }
225
+ const providerCostUsage = this.extractCostUsageFields(providerEntry.usage);
226
+ if (Object.keys(providerCostUsage).length === 0) {
227
+ continue;
228
+ }
229
+ hasProviderUsage = true;
230
+ this.mergeMissingUsageFields(mergedUsage, providerCostUsage);
231
+ }
232
+ }
233
+ if (this.isPlainObject(stepUsage)) {
234
+ return mergedUsage;
235
+ }
236
+ if (hasProviderUsage) {
237
+ return mergedUsage;
238
+ }
239
+ return stepUsage;
240
+ }
241
+ async validateToolCallInput(params) {
242
+ const agentOptions = await this.aiHelper.getAgentOptions({
243
+ agentName: params.agentName,
244
+ });
245
+ const tool = agentOptions.tools?.[params.toolName] ?? undefined;
246
+ const schema = tool?.inputSchema;
247
+ if (!schema) {
248
+ throw new Error(`Tool "${params.toolName}" schema not found`);
249
+ }
250
+ if (!(schema instanceof z.ZodType)) {
251
+ throw new Error(`Tool "${params.toolName}" inputSchema must be a Zod schema`);
252
+ }
253
+ const result = schema.safeParse(params.input);
254
+ if (!result.success) {
255
+ throw new Error(`Invalid tool input for "${params.toolName}": ${result.error.message}`);
256
+ }
257
+ }
24
258
  async listChats(params) {
25
- // Build filter object from params
26
- const filter = {};
259
+ // Build filter object from params. Always exclude subagent chats:
260
+ // the repository translates ROOT into `sessionKind !== SUBAGENT`, which
261
+ // also includes legacy chats where the field is unset.
262
+ const filter = {
263
+ sessionKind: AgentSessionKind.ROOT
264
+ };
27
265
  if (params?.userId) {
266
+ // Chats visible to this user: owner, tenant owner, or participant (multi-user).
28
267
  filter.userId = params.userId;
29
268
  }
30
269
  if (params?.contextKey) {
31
270
  filter.contextKey = params.contextKey;
32
271
  }
33
- // Build query options for sort and limit with defaults
272
+ if (params?.multiUser !== undefined) {
273
+ filter.multiUser = params.multiUser;
274
+ }
275
+ // Build query options for sort and limit with defaults.
34
276
  const options = {
35
277
  sort: {
36
278
  field: params?.sortField ?? 'updatedAt',
37
279
  order: params?.sortOrder ?? SortOrder.Desc
38
280
  },
281
+ skip: params?.skip,
39
282
  limit: params?.limit
40
283
  };
41
- // Use aggregation to fetch chats with related messages
42
- return this.chatRepository.findWithMessages(filter, options);
284
+ const [total, data] = await Promise.all([
285
+ this.chatRepository.count(filter),
286
+ this.chatRepository.find(filter, options)
287
+ ]);
288
+ return {
289
+ cursor: {
290
+ ...(params?.skip != null ? { skip: params.skip } : {}),
291
+ ...(params?.limit != null ? { limit: params.limit } : {}),
292
+ total
293
+ },
294
+ data
295
+ };
43
296
  }
44
297
  async getChat(chatId) {
45
298
  const chat = await this.chatRepository.findById(chatId);
46
299
  if (!chat) {
47
300
  throw new Error('Chat not found');
48
301
  }
49
- const messages = await this.messageRepository.findByChatId(chatId);
50
- return {
51
- ...chat,
52
- messages
53
- };
302
+ return chat;
303
+ }
304
+ /**
305
+ * Get messages for a chat with optional cursor pagination.
306
+ * When limit is omitted, returns all messages (backward compatible).
307
+ * Messages are returned in chronological order (oldest → newest).
308
+ */
309
+ async getMessages(chatId, options) {
310
+ const chat = await this.chatRepository.findById(chatId);
311
+ if (!chat) {
312
+ throw new Error('Chat not found');
313
+ }
314
+ return this.messageRepository.findByChatIdPaginated(chatId, options);
54
315
  }
55
316
  async deleteChat(chatId) {
317
+ // Collect all subagent descendants BEFORE removing the parent, otherwise the
318
+ // `parentChatId` link is severed and we'd orphan the subtree. Subagents can
319
+ // themselves spawn subagents, so traversal must be recursive.
320
+ const descendantIds = await this.collectDescendantChatIds(chatId);
56
321
  const deleted = await this.chatRepository.delete(chatId);
57
322
  if (!deleted) {
58
323
  throw new Error('Chat not found');
59
324
  }
60
- this.artifactStore.deleteArtifacts(chatId);
325
+ // Delete descendant chat rows in parallel. Use `Promise.allSettled` so a
326
+ // missing/already-deleted descendant doesn't abort cleanup of the rest.
327
+ if (descendantIds.length > 0) {
328
+ await Promise.allSettled(descendantIds.map(id => this.chatRepository.delete(id)));
329
+ }
330
+ const allIds = [chatId, ...descendantIds];
331
+ await Promise.all([
332
+ ...allIds.map(id => this.messageRepository.deleteByChatId(id)),
333
+ ...allIds.map(id => this.activityRepository.deleteByGroupId(id)),
334
+ ]);
335
+ for (const id of allIds) {
336
+ this.artifactStore.deleteArtifacts(id);
337
+ }
338
+ }
339
+ /**
340
+ * Walk the subagent tree rooted at `rootChatId` and return every descendant
341
+ * chat id (excluding the root itself). BFS, with parallel fan-out per level
342
+ * so deeply nested trees don't serialize one DB roundtrip per depth.
343
+ */
344
+ async collectDescendantChatIds(rootChatId) {
345
+ const descendants = [];
346
+ let frontier = [rootChatId];
347
+ while (frontier.length > 0) {
348
+ const childLists = await Promise.all(frontier.map(id => this.chatRepository.find({ parentChatId: id })));
349
+ const nextFrontier = [];
350
+ for (const list of childLists) {
351
+ for (const child of list) {
352
+ descendants.push(child.id);
353
+ nextFrontier.push(child.id);
354
+ }
355
+ }
356
+ frontier = nextFrontier;
357
+ }
358
+ return descendants;
61
359
  }
62
360
  async upsertAndGetChat(payload, excludeSocketId) {
63
- const targetUserId = payload.userId ?? 'guest';
64
361
  let chat = null;
65
362
  if (payload.chatId) {
66
363
  chat = await this.chatRepository.findById(payload.chatId);
67
364
  if (!chat) {
68
365
  throw new Error('Chat not found');
69
366
  }
70
- // Ensure chat is associated with the caller's userId
71
- if (chat.userId && chat.userId !== targetUserId) {
72
- throw new Error('Chat does not belong to this user');
73
- }
74
367
  }
75
368
  if (!chat) {
76
369
  chat = await this.createChat(payload, excludeSocketId);
@@ -92,7 +385,7 @@ export class ChatProcessor {
92
385
  if (!content) {
93
386
  return this.getTemporaryTitle(payload.contextKey);
94
387
  }
95
- return AIHelper.generateTitleForMessage(payload.contextKey, [{
388
+ return this.aiHelper.generateTitleForMessage(payload.contextKey, [{
96
389
  role: MessageRole.User,
97
390
  content: content
98
391
  }]);
@@ -103,34 +396,38 @@ export class ChatProcessor {
103
396
  role,
104
397
  content: messageData.content,
105
398
  agentName: messageData.agentName,
399
+ sender: messageData.sender,
400
+ mentions: messageData.mentions ?? [],
401
+ annotations: messageData.annotations,
106
402
  attachments: attachments ?? [],
107
403
  reasoning: "",
108
404
  toolCalls: []
109
405
  });
110
- if (chat.userId) {
111
- socketService.emitMessageUpdate(chat.userId, message, excludeSocketId);
112
- }
406
+ this.emitMessageToParticipants(chat, message, excludeSocketId);
113
407
  return message;
114
408
  }
115
409
  async createChat(payload, excludeSocketId) {
116
- const targetUserId = payload.userId ?? 'guest';
410
+ const targetUserId = payload.userId ?? payload.tenants?.userId ?? 'guest';
117
411
  const contextKey = 'contextKey' in payload ? payload.contextKey : 'default';
118
412
  const metadata = 'metadata' in payload ? payload.metadata : {};
413
+ const requestedParticipantIds = 'participantIds' in payload && Array.isArray(payload.participantIds) ? payload.participantIds : [];
414
+ const participantIds = Array.from(new Set([targetUserId, ...requestedParticipantIds].filter(Boolean)));
119
415
  const chat = await this.chatRepository.create({
120
416
  title: this.getTemporaryTitle(contextKey),
121
417
  type: ChatType.Chat,
122
418
  status: AgentStatus.Streaming,
419
+ sessionKind: AgentSessionKind.ROOT,
123
420
  contextKey,
124
421
  userId: targetUserId,
422
+ tenants: {
423
+ ...(payload.tenants ?? {}),
424
+ userId: targetUserId,
425
+ },
426
+ participantIds,
125
427
  metadata,
126
428
  ...(payload.model ? { model: payload.model } : {})
127
429
  });
128
- await kafkaService.sendChatTitleGenerationEvent(chat.id);
129
- const initialChatResponse = {
130
- ...chat,
131
- messages: []
132
- };
133
- socketService.emitChatUpdate(targetUserId, initialChatResponse, excludeSocketId);
430
+ this.emitChatToParticipants(chat, chat, excludeSocketId);
134
431
  return chat;
135
432
  }
136
433
  /**
@@ -185,33 +482,163 @@ export class ChatProcessor {
185
482
  await this.messageRepository.update(message.id, { toolCalls });
186
483
  // Bump chat updatedAt so listChats ordering reflects the action.
187
484
  await this.chatRepository.update(chat.id, { updatedAt: now });
188
- socketService.emitMessageUpdate(chat.userId, updatedMessage, params.excludeSocketId);
485
+ this.emitMessageToParticipants(chat, updatedMessage, params.excludeSocketId);
189
486
  if (params.systemMessage) {
190
487
  //todo discuss support for system messages
191
488
  //await this.createMessage(chat, MessageRole.System, params.systemMessage, undefined, params.excludeSocketId);
192
489
  }
193
490
  return { ok: true };
194
491
  }
492
+ /**
493
+ * Update selected fields (input/output/status) on a specific tool call.
494
+ */
495
+ async updateToolCall(params) {
496
+ if (params.input === undefined &&
497
+ params.output === undefined &&
498
+ params.status === undefined &&
499
+ params.subagentChatId === undefined) {
500
+ throw new Error('At least one tool call update field must be provided');
501
+ }
502
+ const chat = await this.chatRepository.findById(params.chatId);
503
+ if (!chat) {
504
+ throw new Error('Chat not found');
505
+ }
506
+ if (params.userId && !this.hasChatAccess(chat, params.userId)) {
507
+ throw new Error('Chat does not belong to this user');
508
+ }
509
+ const message = await this.messageRepository.findById(params.messageId);
510
+ if (!message || message.chat !== params.chatId) {
511
+ throw new Error('Message not found or does not belong to this chat');
512
+ }
513
+ const targetToolCall = (message.toolCalls ?? []).find((tc) => tc.id === params.toolCallId);
514
+ if (!targetToolCall) {
515
+ throw new Error('Tool call not found in message');
516
+ }
517
+ if (params.input !== undefined) {
518
+ await this.validateToolCallInput({
519
+ agentName: message.agentName,
520
+ toolName: targetToolCall.name,
521
+ input: params.input,
522
+ });
523
+ }
524
+ const updatedMessage = await this.messageRepository.updateToolCall(params.messageId, params.toolCallId, {
525
+ ...(params.input !== undefined ? { input: params.input } : {}),
526
+ ...(params.output !== undefined ? { output: params.output } : {}),
527
+ ...(params.status !== undefined ? { status: params.status } : {}),
528
+ ...(params.subagentChatId !== undefined ? { subagentChatId: params.subagentChatId } : {}),
529
+ });
530
+ if (!updatedMessage) {
531
+ throw new Error('Tool call not found in message');
532
+ }
533
+ const now = new Date().toISOString();
534
+ await this.chatRepository.update(chat.id, { updatedAt: now });
535
+ this.emitMessageToParticipants(chat, updatedMessage, params.excludeSocketId);
536
+ return updatedMessage;
537
+ }
538
+ async storeSubagentResponse(params) {
539
+ const { subagentChat, content, error, context, executionContext } = params;
540
+ if (!subagentChat.parentChatId || !subagentChat.parentMessageId) {
541
+ throw new Error(`Parent chat or message not found for subagent ${subagentChat.id}`);
542
+ }
543
+ const chat = await this.chatRepository.findById(subagentChat.parentChatId);
544
+ if (!chat) {
545
+ throw new Error(`Parent chat not found for subagent ${subagentChat.id}`);
546
+ }
547
+ const assistantMessage = await this.messageRepository.findById(subagentChat.parentMessageId);
548
+ if (!assistantMessage || assistantMessage.chat !== subagentChat.parentChatId) {
549
+ throw new Error(`Assistant message with id ${subagentChat.parentMessageId} not found`);
550
+ }
551
+ const toolCall = assistantMessage.toolCalls?.find(tc => tc.id === subagentChat.parentToolCallId);
552
+ if (!toolCall) {
553
+ throw new Error(`Tool call with id ${subagentChat.parentToolCallId} not found`);
554
+ }
555
+ if (error) {
556
+ toolCall.output = {
557
+ type: 'error',
558
+ message: error,
559
+ };
560
+ toolCall.status = AgentToolCallStatus.Failed;
561
+ }
562
+ else {
563
+ toolCall.output = content;
564
+ toolCall.status = AgentToolCallStatus.Succeeded;
565
+ }
566
+ await this.messageRepository.update(assistantMessage.id, assistantMessage);
567
+ const agentOptions = await this.aiHelper.getAgentOptions({
568
+ agentName: assistantMessage.agentName,
569
+ context: context,
570
+ executionContext: executionContext,
571
+ });
572
+ const executionTenants = this.getExecutionTenants(params);
573
+ const abortController = this.agentStore.registerAgentProcess(chat.id);
574
+ const existingMessages = await this.messageRepository.findByChatId(chat.id);
575
+ // Limit context to prevent exceeding token limits
576
+ const limitedMessages = ContextLimiter.limitContext(existingMessages, {
577
+ maxMessages: this.config.ai.maxContextMessages,
578
+ keepFirstUserMessage: true,
579
+ keepSystemMessages: true,
580
+ });
581
+ agentOptions.onStepFinish = (stepResult) => {
582
+ return this.storeStepActivity({
583
+ chat,
584
+ parentId: assistantMessage.activity,
585
+ stepResult,
586
+ name: ActivityOperationName.STEP_FINISHED,
587
+ sourceId: assistantMessage.id,
588
+ sourceType: 'AgentMessage',
589
+ tenants: executionTenants,
590
+ });
591
+ };
592
+ const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(this.getChatUserId(chat) ?? chat.userId ?? 'guest', agentOptions.name);
593
+ agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
594
+ await this.streamMessageStep({
595
+ chat,
596
+ existingMessages: limitedMessages,
597
+ assistantMessage,
598
+ agentOptions,
599
+ signal: abortController.signal,
600
+ tenants: executionTenants,
601
+ executionContext,
602
+ context,
603
+ });
604
+ }
195
605
  async streamMessageStep(params) {
196
606
  const { chat, assistantMessage, existingMessages, signal, excludeSocketId, agentOptions } = params;
607
+ const persistAndBroadcastToolCalls = async () => {
608
+ await this.messageRepository.update(assistantMessage.id, { toolCalls: assistantMessage.toolCalls ?? [] });
609
+ this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
610
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
611
+ };
197
612
  if (signal.aborted) {
198
613
  return;
199
614
  }
200
- const result = await AIHelper.streamAssistantResponse(existingMessages, signal, agentOptions);
615
+ const result = await this.aiHelper.streamAssistantResponse(existingMessages, signal, {
616
+ ...agentOptions,
617
+ executionContext: params.executionContext,
618
+ });
201
619
  try {
202
620
  for await (const chunk of result.fullStream) {
203
621
  if (chunk.type === 'error') {
204
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: chunk.error });
622
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: chunk.error });
205
623
  assistantMessage.content = chunk.error.message || 'Sorry, I cannot process you request due to the error';
206
624
  await this.messageRepository.update(assistantMessage.id, assistantMessage);
207
- await this.chatRepository.update(chat.id, { status: AgentStatus.Error });
625
+ await this.updateChatAndEmit(chat, { status: AgentStatus.Error }, excludeSocketId);
626
+ if (chat.sessionKind === AgentSessionKind.SUBAGENT) {
627
+ await this.storeSubagentResponse({
628
+ subagentChat: chat,
629
+ content: undefined,
630
+ error: chunk.error,
631
+ context: params.context,
632
+ executionContext: params.executionContext,
633
+ });
634
+ }
208
635
  continue;
209
636
  }
210
637
  if (chunk.type === 'abort') {
211
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Aborted, data: undefined });
212
- await this.chatRepository.update(chat.id, { status: AgentStatus.Aborted });
638
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Aborted, data: undefined });
639
+ await this.updateChatAndEmit(chat, { status: AgentStatus.Aborted }, excludeSocketId);
213
640
  await this.messageRepository.update(assistantMessage.id, assistantMessage);
214
- socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
641
+ this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
215
642
  continue;
216
643
  }
217
644
  if (chunk.type === 'finish') {
@@ -228,38 +655,76 @@ export class ChatProcessor {
228
655
  ((totalUsage.promptTokens ?? 0) + (totalUsage.completionTokens ?? 0));
229
656
  }
230
657
  await this.messageRepository.update(assistantMessage.id, { ...assistantMessage });
658
+ this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
231
659
  if (chunk.finishReason === 'stop') {
232
- if (assistantMessage.toolCalls?.some(toolCall => toolCall.requiresConfirmation && toolCall.approvalId && !toolCall.output)) {
233
- await this.chatRepository.update(chat.id, { status: AgentStatus.WaitingForUserAction });
660
+ let nextChat = await this.updateChatAndEmit(chat, { status: AgentStatus.Finished }, excludeSocketId);
661
+ if (chat.title && this.isTemporaryTitle(chat.title)) {
662
+ const title = await this.aiHelper.generateTitleForMessage(chat.contextKey, existingMessages.filter(m => m.role === MessageRole.User), (stepResult) => {
663
+ return this.storeStepActivity({
664
+ chat,
665
+ stepResult,
666
+ name: ActivityOperationName.TITLE_GENERATION,
667
+ sourceId: chat.id,
668
+ sourceType: 'Chat',
669
+ tenants: params.tenants || {},
670
+ });
671
+ }, params.executionContext);
672
+ nextChat = await this.updateChatAndEmit(nextChat, { title }, excludeSocketId);
234
673
  }
235
- else if (!assistantMessage.content && assistantMessage.toolCalls?.length && assistantMessage.toolCalls[assistantMessage.toolCalls?.length - 1].output) {
236
- // handle tool-calls stop, openrouter provider does not return valid type
237
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
238
- await this.streamMessageStep(params);
239
- continue;
240
- }
241
- else {
242
- await this.chatRepository.update(chat.id, { status: AgentStatus.Finished });
674
+ if (chat.sessionKind === AgentSessionKind.SUBAGENT) {
675
+ await this.storeSubagentResponse({
676
+ subagentChat: chat,
677
+ content: assistantMessage.content,
678
+ error: undefined,
679
+ context: params.context,
680
+ executionContext: params.executionContext,
681
+ });
243
682
  }
244
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
245
- continue;
246
683
  }
247
684
  if (chunk.finishReason === 'tool-calls') {
248
- if (assistantMessage.toolCalls?.some(toolCall => toolCall.requiresConfirmation && toolCall.approvalId && !toolCall.output)) {
249
- await this.chatRepository.update(chat.id, { status: AgentStatus.WaitingForUserAction });
250
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
685
+ const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.output === undefined && toolCall.status === AgentToolCallStatus.Running);
686
+ if (!toolCall) {
687
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
688
+ await this.storeSubagentResponse({
689
+ subagentChat: chat,
690
+ content: JSON.stringify(assistantMessage.toolCalls),
691
+ error: undefined,
692
+ context: params.context,
693
+ executionContext: params.executionContext,
694
+ });
695
+ continue;
696
+ }
697
+ const toolConfig = ConfigStore.getInstance().getToolConfig(assistantMessage.agentName || '', toolCall.name);
698
+ if (toolCall.requiresConfirmation && toolCall.approvalId) {
699
+ await this.updateChatAndEmit(chat, { status: AgentStatus.WaitingForUserAction }, excludeSocketId);
700
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
251
701
  }
252
- else {
253
- await agentStore.shareAgentProcessEvent(chat.id, {
254
- type: AgentProcessEventType.Update,
255
- data: assistantMessage
702
+ else if (toolConfig && toolConfig.type === AgentToolType.SUBAGENT) {
703
+ void this.startSubagentProcess({
704
+ parentChat: chat,
705
+ parentMessage: assistantMessage,
706
+ parentToolCallId: toolCall.id,
707
+ toolName: toolCall.name,
708
+ input: toolCall.input,
709
+ subAgentConfig: toolConfig.data.subAgent,
710
+ context: params.context,
711
+ executionContext: params.executionContext,
712
+ }).catch(error => {
713
+ logger.error(error);
714
+ //todo: store error in response
256
715
  });
257
- await this.streamMessageStep(params);
258
716
  }
259
- continue;
260
717
  }
261
718
  //todo: Handle other finish reasons // length, content, error, other, undefined
262
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
719
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
720
+ if (assistantMessage.activity) {
721
+ const groupedMeta = await this.activityRepository.getGroupedMetadataByParentId(assistantMessage.activity);
722
+ await this.activityRepository.updateMetadata(assistantMessage.activity, {
723
+ usage: groupedMeta,
724
+ finishReason: chunk.finishReason,
725
+ responseTimestamp: new Date().toISOString(),
726
+ });
727
+ }
263
728
  continue;
264
729
  }
265
730
  if (chunk.type === 'text-delta') {
@@ -268,55 +733,85 @@ export class ChatProcessor {
268
733
  ...assistantMessage,
269
734
  content: assistantMessage.content
270
735
  };
271
- socketService.emitMessageUpdate(chat.userId, updatedMessage, excludeSocketId);
272
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: updatedMessage });
736
+ this.emitMessageToParticipants(chat, updatedMessage, excludeSocketId);
737
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: updatedMessage });
273
738
  await this.messageRepository.update(assistantMessage.id, { content: assistantMessage.content });
274
739
  continue;
275
740
  }
276
741
  if (chunk.type === 'tool-input-start') {
277
742
  if (!assistantMessage.toolCalls)
278
743
  assistantMessage.toolCalls = [];
279
- assistantMessage.toolCalls.push({
280
- id: chunk.id,
281
- name: chunk.toolName,
282
- status: AgentToolCallStatus.Pending,
283
- input: {},
284
- });
744
+ const existingToolCall = assistantMessage.toolCalls.find((toolCall) => toolCall.id === chunk.id);
745
+ if (existingToolCall) {
746
+ existingToolCall.name = chunk.toolName;
747
+ if (!existingToolCall.status) {
748
+ existingToolCall.status = AgentToolCallStatus.Pending;
749
+ }
750
+ existingToolCall.input = existingToolCall.input ?? {};
751
+ }
752
+ else {
753
+ assistantMessage.toolCalls.push({
754
+ id: chunk.id,
755
+ name: chunk.toolName,
756
+ status: AgentToolCallStatus.Pending,
757
+ input: {},
758
+ });
759
+ }
760
+ await persistAndBroadcastToolCalls();
285
761
  continue;
286
762
  }
287
- if (chunk.type === 'tool-call') { //Todo possible place to handle user-approval
288
- const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
289
- if (toolCall) {
763
+ if (chunk.type === 'tool-call') {
764
+ if (!assistantMessage.toolCalls)
765
+ assistantMessage.toolCalls = [];
766
+ let toolCall = assistantMessage.toolCalls.find(toolCall => toolCall.id === chunk.toolCallId);
767
+ if (!toolCall) {
768
+ toolCall = {
769
+ id: chunk.toolCallId,
770
+ name: chunk.toolName,
771
+ status: AgentToolCallStatus.Running,
772
+ input: chunk.input,
773
+ };
774
+ assistantMessage.toolCalls.push(toolCall);
775
+ }
776
+ else {
290
777
  toolCall.status = AgentToolCallStatus.Running;
291
778
  toolCall.input = chunk.input;
292
779
  }
780
+ await persistAndBroadcastToolCalls();
293
781
  continue;
294
782
  }
295
783
  if (chunk.type === 'tool-error') {
296
784
  const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
297
785
  if (toolCall) {
298
786
  toolCall.status = AgentToolCallStatus.Failed;
299
- toolCall.error = JSON.stringify(chunk.error);
787
+ toolCall.error = chunk.error.message || JSON.stringify(chunk.error);
788
+ await persistAndBroadcastToolCalls();
300
789
  }
301
790
  continue;
302
791
  }
303
792
  if (chunk.type === 'tool-approval-request') {
304
793
  const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCall.toolCallId);
305
794
  if (toolCall) {
795
+ const toolConfig = ConfigStore.getInstance().getToolConfig(assistantMessage.agentName || '', toolCall.name);
796
+ if (toolConfig && toolConfig.type === AgentToolType.SUBAGENT) {
797
+ // TODO: allow needsApproval flow for the subagents too
798
+ continue;
799
+ }
306
800
  toolCall.requiresConfirmation = true;
307
801
  toolCall.approvalId = chunk.approvalId;
802
+ if (toolCall.name === AgentToolType.REQUEST_CLARIFICATION) {
803
+ toolCall.requiresUserAction = true;
804
+ }
805
+ await persistAndBroadcastToolCalls();
308
806
  }
309
807
  continue;
310
808
  }
311
809
  if (chunk.type === 'tool-result') {
312
810
  const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
313
811
  if (toolCall) {
314
- if (chunk.output.requiresApproval) {
315
- toolCall.requiresConfirmation = true;
316
- continue;
317
- }
318
812
  toolCall.status = AgentToolCallStatus.Succeeded;
319
813
  toolCall.output = chunk.output;
814
+ await persistAndBroadcastToolCalls();
320
815
  }
321
816
  continue;
322
817
  }
@@ -324,9 +819,9 @@ export class ChatProcessor {
324
819
  if (!assistantMessage.reasoning)
325
820
  assistantMessage.reasoning = '';
326
821
  assistantMessage.reasoning += chunk.text;
327
- socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
822
+ this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
328
823
  await this.messageRepository.update(assistantMessage.id, { reasoning: assistantMessage.reasoning });
329
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
824
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
330
825
  continue;
331
826
  }
332
827
  }
@@ -335,7 +830,7 @@ export class ChatProcessor {
335
830
  if (streamError instanceof Error && streamError.name === 'AbortError') {
336
831
  return;
337
832
  }
338
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: streamError });
833
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: streamError });
339
834
  throw streamError;
340
835
  }
341
836
  }
@@ -347,9 +842,10 @@ export class ChatProcessor {
347
842
  if (!assistantMessage || assistantMessage.chat !== chat.id) {
348
843
  throw new Error(`Assistant message with id ${payload.messageId} not found`);
349
844
  }
350
- const agentOptions = await AIHelper.getAgentOptions({
845
+ const agentOptions = await this.aiHelper.getAgentOptions({
351
846
  agentName: assistantMessage.agentName,
352
- context: payload.context
847
+ context: payload.context,
848
+ executionContext: payload.executionContext,
353
849
  });
354
850
  const toolCall = assistantMessage.toolCalls?.find(tc => tc.approvalId === payload.approvalId);
355
851
  if (!toolCall) {
@@ -360,14 +856,20 @@ export class ChatProcessor {
360
856
  };
361
857
  }
362
858
  toolCall.approved = payload.approved;
363
- toolCall.reason = payload.reason;
859
+ toolCall.userResponse = payload.userResponse;
364
860
  if (!toolCall.approved) {
365
861
  toolCall.output = {
366
862
  type: 'execution-denied',
367
- reason: toolCall.reason
863
+ reason: toolCall.userResponse
368
864
  };
369
865
  toolCall.status = AgentToolCallStatus.Failed;
370
866
  }
867
+ else if (toolCall.requiresUserAction) {
868
+ toolCall.output = {
869
+ userResponse: toolCall.userResponse
870
+ };
871
+ toolCall.status = AgentToolCallStatus.Succeeded;
872
+ }
371
873
  else {
372
874
  const executeFunction = agentOptions.tools?.[toolCall.name]?.execute;
373
875
  if (executeFunction) {
@@ -399,33 +901,333 @@ export class ChatProcessor {
399
901
  };
400
902
  }
401
903
  async processNewUserMessage(chat, payload, prevMessages, excludeSocketId) {
904
+ const executionTenants = this.getExecutionTenants(payload);
905
+ const chatUserId = this.getChatUserId(chat) ?? 'guest';
402
906
  const userMessage = await this.createMessage(chat, MessageRole.User, { content: payload.content, agentName: undefined }, payload.attachments);
403
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: userMessage });
404
- const agentOptions = await AIHelper.getAgentOptions({
907
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: userMessage });
908
+ let assistantMessage = await this.createMessage(chat, MessageRole.Assistant, { content: '', agentName: undefined }, undefined, excludeSocketId);
909
+ const parentActivity = await this.activityRepository.create({
910
+ ownerId: chatUserId,
911
+ groupId: chat.id,
912
+ name: ActivityOperationName.MESSAGE_PROCESSING,
913
+ tenants: executionTenants,
914
+ sourceId: userMessage.id,
915
+ sourceType: 'AgentMessage',
916
+ metadata: {
917
+ contextKey: chat.contextKey,
918
+ modelId: this.aiHelper.getLanguageModelId(chat.model),
919
+ },
920
+ });
921
+ const agentOptions = await this.aiHelper.getAgentOptions({
405
922
  contextKey: chat.contextKey,
406
- messages: await AIHelper.convertMessages([...prevMessages, userMessage], true),
923
+ messages: [...prevMessages, userMessage],
407
924
  context: payload.context,
408
925
  agentName: undefined,
409
926
  modelId: chat.model,
927
+ executionContext: payload.executionContext,
928
+ }, {
929
+ onStepFinish: async (stepResult) => {
930
+ await this.storeStepActivity({
931
+ chat,
932
+ parentId: parentActivity.id,
933
+ stepResult,
934
+ name: ActivityOperationName.AGENT_SELECTION,
935
+ sourceId: userMessage.id,
936
+ sourceType: 'AgentMessage',
937
+ tenants: executionTenants,
938
+ });
939
+ }
410
940
  });
411
- const assistantMessage = await this.createMessage(chat, MessageRole.Assistant, { content: ' ', agentName: agentOptions.name }, undefined, excludeSocketId);
412
- assistantMessage.content = '';
941
+ const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
942
+ agentName: agentOptions.name,
943
+ activity: parentActivity.id
944
+ });
945
+ await this.activityRepository.update(parentActivity.id, {
946
+ metadata: {
947
+ ...parentActivity.metadata,
948
+ agentOptions: {
949
+ name: agentOptions.name,
950
+ modelId: this.aiHelper.getLanguageModelId(agentOptions.model),
951
+ temperature: agentOptions.temperature,
952
+ maxOutputTokens: agentOptions.maxOutputTokens,
953
+ topP: agentOptions.topP,
954
+ topK: agentOptions.topK,
955
+ presencePenalty: agentOptions.presencePenalty,
956
+ frequencyPenalty: agentOptions.frequencyPenalty,
957
+ stopSequences: agentOptions.stopSequences,
958
+ seed: agentOptions.seed,
959
+ },
960
+ },
961
+ });
962
+ if (!updatedAssistantMessage) {
963
+ throw new Error(`Assistant message with id ${assistantMessage.id} not found after update`);
964
+ }
965
+ assistantMessage = updatedAssistantMessage;
413
966
  return {
414
967
  userMessage,
415
968
  assistantMessage,
416
969
  agentOptions,
417
970
  };
418
971
  }
972
+ getAgentNamesForContext(contextKey) {
973
+ try {
974
+ return ConfigStore.getInstance().getAgentsForContext(contextKey).map((config) => config.name);
975
+ }
976
+ catch {
977
+ return [];
978
+ }
979
+ }
980
+ normalizeMentionName(value) {
981
+ return value.trim().replace(/^@/, '').toLowerCase();
982
+ }
983
+ extractMentionNames(content) {
984
+ const mentions = content.matchAll(/(^|[\s([{])@([a-zA-Z0-9_.-]+)/g);
985
+ return Array.from(mentions, (match) => match[2]).filter(Boolean);
986
+ }
987
+ getExplicitAgentMention(params) {
988
+ const agentNames = this.getAgentNamesForContext(params.contextKey);
989
+ const normalizedAgentNames = new Map(agentNames.map((name) => [this.normalizeMentionName(name), name]));
990
+ for (const mention of params.mentions ?? []) {
991
+ if (mention.type !== 'agent')
992
+ continue;
993
+ const normalized = this.normalizeMentionName(mention.name);
994
+ return { mentioned: true, agentName: normalizedAgentNames.get(normalized) };
995
+ }
996
+ for (const name of this.extractMentionNames(params.content)) {
997
+ const normalized = this.normalizeMentionName(name);
998
+ if (normalized === 'agent') {
999
+ return { mentioned: true };
1000
+ }
1001
+ const agentName = normalizedAgentNames.get(normalized);
1002
+ if (agentName) {
1003
+ return { mentioned: true, agentName };
1004
+ }
1005
+ }
1006
+ return { mentioned: false };
1007
+ }
1008
+ hasNonAgentMention(params) {
1009
+ const agentNames = this.getAgentNamesForContext(params.contextKey);
1010
+ const normalizedAgentNames = new Set(['agent', ...agentNames.map((name) => this.normalizeMentionName(name))]);
1011
+ if ((params.mentions ?? []).some((mention) => mention.type === 'user')) {
1012
+ return true;
1013
+ }
1014
+ return this.extractMentionNames(params.content).some((name) => {
1015
+ const normalized = this.normalizeMentionName(name);
1016
+ return !normalizedAgentNames.has(normalized);
1017
+ });
1018
+ }
1019
+ renderMessageForDecision(message) {
1020
+ const sender = message.sender?.displayName || message.sender?.id || message.agentName || message.role;
1021
+ return `${sender}: ${message.content}`;
1022
+ }
1023
+ async analyzeAgentShouldRespond(params) {
1024
+ try {
1025
+ const recentMessages = [...params.previousMessages.slice(-8), params.userMessage]
1026
+ .map((message) => this.renderMessageForDecision(message))
1027
+ .join('\n');
1028
+ const response = await this.aiHelper.getAssistantResponse([
1029
+ {
1030
+ role: MessageRole.User,
1031
+ content: `Conversation:\n${recentMessages}\n\nShould the AI agent respond to the latest message?`
1032
+ }
1033
+ ], {
1034
+ system: [
1035
+ 'You decide whether an AI agent should reply in a multi-user chat.',
1036
+ 'Reply with exactly YES or NO.',
1037
+ 'YES only when the latest message asks for help, requests agent action, or is clearly addressed to the AI.',
1038
+ 'NO when the latest message is human-to-human chatter, status, acknowledgement, or otherwise does not need the AI.'
1039
+ ].join('\n'),
1040
+ temperature: 0,
1041
+ maxOutputTokens: 5,
1042
+ model: params.chat.model,
1043
+ executionContext: params.executionContext
1044
+ });
1045
+ return /^yes\b/i.test(response.trim());
1046
+ }
1047
+ catch (error) {
1048
+ logger.warn({ error, chatId: params.chat.id }, 'Agent response decision failed');
1049
+ return false;
1050
+ }
1051
+ }
1052
+ async shouldRunAgentForMultiUserMessage(params) {
1053
+ const firstUserMessage = !params.previousMessages.some((message) => message.role === MessageRole.User);
1054
+ if (firstUserMessage) {
1055
+ return { shouldRun: true };
1056
+ }
1057
+ const explicitAgent = this.getExplicitAgentMention({
1058
+ content: params.payload.content,
1059
+ mentions: params.payload.mentions,
1060
+ contextKey: params.chat.contextKey,
1061
+ });
1062
+ if (explicitAgent.mentioned) {
1063
+ return { shouldRun: true, agentName: explicitAgent.agentName };
1064
+ }
1065
+ if (this.hasNonAgentMention({
1066
+ content: params.payload.content,
1067
+ mentions: params.payload.mentions,
1068
+ contextKey: params.chat.contextKey,
1069
+ })) {
1070
+ return { shouldRun: false };
1071
+ }
1072
+ const shouldRun = await this.analyzeAgentShouldRespond({
1073
+ chat: params.chat,
1074
+ previousMessages: params.previousMessages,
1075
+ userMessage: params.userMessage,
1076
+ executionContext: params.payload.executionContext,
1077
+ });
1078
+ return { shouldRun };
1079
+ }
1080
+ async prepareAgentResponseForUserMessage(chat, payload, prevMessages, userMessage, excludeSocketId, agentName) {
1081
+ const executionTenants = this.getExecutionTenants(payload);
1082
+ let assistantMessage = await this.createMessage(chat, MessageRole.Assistant, {
1083
+ content: '',
1084
+ agentName: agentName,
1085
+ sender: {
1086
+ id: agentName ?? 'agent',
1087
+ type: 'agent',
1088
+ displayName: agentName ?? 'Agent',
1089
+ }
1090
+ }, undefined, excludeSocketId);
1091
+ const ownerId = payload.userId ?? chat.userId ?? this.getChatUserId(chat) ?? 'guest';
1092
+ const parentActivity = await this.activityRepository.create({
1093
+ ownerId,
1094
+ groupId: chat.id,
1095
+ name: ActivityOperationName.MESSAGE_PROCESSING,
1096
+ tenants: executionTenants,
1097
+ sourceId: userMessage.id,
1098
+ sourceType: 'AgentMessage',
1099
+ metadata: {
1100
+ contextKey: chat.contextKey,
1101
+ modelId: this.aiHelper.getLanguageModelId(chat.model),
1102
+ },
1103
+ });
1104
+ const agentOptions = await this.aiHelper.getAgentOptions(agentName
1105
+ ? {
1106
+ agentName,
1107
+ modelId: chat.model,
1108
+ context: payload.context,
1109
+ executionContext: payload.executionContext,
1110
+ }
1111
+ : {
1112
+ contextKey: chat.contextKey,
1113
+ messages: [...prevMessages, userMessage],
1114
+ context: payload.context,
1115
+ agentName: undefined,
1116
+ modelId: chat.model,
1117
+ executionContext: payload.executionContext,
1118
+ }, {
1119
+ onStepFinish: async (stepResult) => {
1120
+ await this.storeStepActivity({
1121
+ chat,
1122
+ parentId: parentActivity.id,
1123
+ stepResult,
1124
+ name: ActivityOperationName.AGENT_SELECTION,
1125
+ sourceId: userMessage.id,
1126
+ sourceType: 'AgentMessage',
1127
+ tenants: executionTenants,
1128
+ });
1129
+ }
1130
+ });
1131
+ const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
1132
+ agentName: agentOptions.name,
1133
+ activity: parentActivity.id,
1134
+ sender: {
1135
+ id: agentOptions.name,
1136
+ type: 'agent',
1137
+ displayName: agentOptions.name,
1138
+ }
1139
+ });
1140
+ await this.activityRepository.update(parentActivity.id, {
1141
+ metadata: {
1142
+ ...parentActivity.metadata,
1143
+ agentOptions: {
1144
+ name: agentOptions.name,
1145
+ modelId: this.aiHelper.getLanguageModelId(agentOptions.model),
1146
+ temperature: agentOptions.temperature,
1147
+ maxOutputTokens: agentOptions.maxOutputTokens,
1148
+ topP: agentOptions.topP,
1149
+ topK: agentOptions.topK,
1150
+ presencePenalty: agentOptions.presencePenalty,
1151
+ frequencyPenalty: agentOptions.frequencyPenalty,
1152
+ stopSequences: agentOptions.stopSequences,
1153
+ seed: agentOptions.seed,
1154
+ },
1155
+ },
1156
+ });
1157
+ if (!updatedAssistantMessage) {
1158
+ throw new Error(`Assistant message with id ${assistantMessage.id} not found after update`);
1159
+ }
1160
+ assistantMessage = updatedAssistantMessage;
1161
+ return {
1162
+ assistantMessage,
1163
+ agentOptions,
1164
+ executionTenants,
1165
+ };
1166
+ }
1167
+ async streamAgentResponseForUserMessage(params) {
1168
+ const { assistantMessage, agentOptions, executionTenants } = await this.prepareAgentResponseForUserMessage(params.chat, params.payload, params.previousMessages, params.userMessage, params.excludeSocketId, params.agentName);
1169
+ const messagesForAgent = [...params.previousMessages, params.userMessage];
1170
+ agentOptions.onStepFinish = (stepResult) => {
1171
+ return this.storeStepActivity({
1172
+ chat: params.chat,
1173
+ parentId: assistantMessage.activity,
1174
+ stepResult,
1175
+ name: ActivityOperationName.STEP_FINISHED,
1176
+ sourceId: assistantMessage.id,
1177
+ sourceType: 'AgentMessage',
1178
+ tenants: executionTenants,
1179
+ });
1180
+ };
1181
+ const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(params.payload.userId ?? params.chat.userId ?? this.getChatUserId(params.chat) ?? 'guest', agentOptions.name);
1182
+ agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
1183
+ await this.streamMessageStep({
1184
+ chat: params.chat,
1185
+ existingMessages: messagesForAgent,
1186
+ assistantMessage,
1187
+ agentOptions,
1188
+ excludeSocketId: params.excludeSocketId,
1189
+ signal: params.signal,
1190
+ tenants: executionTenants,
1191
+ executionContext: params.payload.executionContext,
1192
+ context: params.payload.context,
1193
+ });
1194
+ }
1195
+ async storeStepActivity(params) {
1196
+ try {
1197
+ const stepResult = params.stepResult;
1198
+ const mergedUsage = this.mergeUsageWithProviderMetadata(stepResult.usage, stepResult.providerMetadata);
1199
+ const activity = await this.activityRepository.create({
1200
+ ownerId: this.getChatUserId(params.chat) ?? 'guest',
1201
+ groupId: params.chat.id,
1202
+ name: params.name,
1203
+ tenants: params.tenants || {},
1204
+ sourceId: params.sourceId,
1205
+ sourceType: params.sourceType,
1206
+ metadata: {
1207
+ finishReason: stepResult.finishReason,
1208
+ usage: mergedUsage,
1209
+ responseTimestamp: stepResult.response.timestamp,
1210
+ modelId: stepResult.response.modelId,
1211
+ messages: this.debug ? stepResult.request?.body?.messages : undefined,
1212
+ },
1213
+ parentId: params.parentId,
1214
+ });
1215
+ }
1216
+ catch (error) {
1217
+ logger.error({ error, chatId: params.chat.id, parentId: params.parentId }, 'Failed to store step activity');
1218
+ }
1219
+ }
419
1220
  async streamMessage(chat, payload, excludeSocketId, onAgentStart) {
420
1221
  try {
421
- const abortController = agentStore.registerAgentProcess(chat.id);
1222
+ const executionTenants = this.getExecutionTenants(payload);
1223
+ const abortController = this.agentStore.registerAgentProcess(chat.id);
422
1224
  onAgentStart?.();
423
1225
  let assistantMessage;
424
1226
  let agentOptions;
425
1227
  const existingMessages = await this.messageRepository.findByChatId(chat.id);
426
1228
  // Limit context to prevent exceeding token limits
427
1229
  const limitedMessages = ContextLimiter.limitContext(existingMessages, {
428
- maxMessages: config.ai.maxContextMessages,
1230
+ maxMessages: this.config.ai.maxContextMessages,
429
1231
  keepFirstUserMessage: true,
430
1232
  keepSystemMessages: true,
431
1233
  });
@@ -447,6 +1249,19 @@ export class ChatProcessor {
447
1249
  agentOptions = result.agentOptions;
448
1250
  limitedMessages.push(result.userMessage);
449
1251
  }
1252
+ agentOptions.onStepFinish = (stepResult) => {
1253
+ return this.storeStepActivity({
1254
+ chat,
1255
+ parentId: assistantMessage.activity,
1256
+ stepResult,
1257
+ name: ActivityOperationName.STEP_FINISHED,
1258
+ sourceId: assistantMessage.id,
1259
+ sourceType: 'AgentMessage',
1260
+ tenants: executionTenants,
1261
+ });
1262
+ };
1263
+ const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(this.getChatUserId(chat) ?? 'guest', agentOptions.name);
1264
+ agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
450
1265
  await this.streamMessageStep({
451
1266
  chat,
452
1267
  existingMessages: limitedMessages,
@@ -454,12 +1269,30 @@ export class ChatProcessor {
454
1269
  agentOptions,
455
1270
  excludeSocketId: excludeSocketId,
456
1271
  signal: abortController.signal,
1272
+ tenants: executionTenants,
1273
+ executionContext: payload.executionContext,
1274
+ context: payload.context,
457
1275
  });
458
1276
  }
459
1277
  catch (error) {
460
1278
  throw error;
461
1279
  }
462
1280
  }
1281
+ getAvailableTools(agentOptions, userPreferences) {
1282
+ const toolNames = Object.keys(agentOptions.tools || {});
1283
+ const availableToolsMap = Object.fromEntries(toolNames.map(key => [key, true]));
1284
+ const agentsConfiguration = ConfigStore.getInstance().getAgentConfigByName(agentOptions.name);
1285
+ agentsConfiguration.tools.forEach((tool) => {
1286
+ const toolTitle = getAgentToolName(tool);
1287
+ if (tool.disabledByDefault) {
1288
+ availableToolsMap[toolTitle] = false;
1289
+ }
1290
+ if (tool.userConfigurable && userPreferences?.tools?.[toolTitle] !== undefined) {
1291
+ availableToolsMap[toolTitle] = userPreferences.tools[toolTitle];
1292
+ }
1293
+ });
1294
+ return Object.keys(availableToolsMap).filter(key => availableToolsMap[key]);
1295
+ }
463
1296
  listArtifacts(chatId) {
464
1297
  return this.artifactStore.listArtifacts(chatId);
465
1298
  }
@@ -490,7 +1323,7 @@ export class ChatProcessor {
490
1323
  return null;
491
1324
  }
492
1325
  }
493
- getMessageStream(chatId) {
1326
+ getMessageStream(chatId, signal) {
494
1327
  const stream = new PassThrough();
495
1328
  let ended = false;
496
1329
  const endStream = (error) => {
@@ -511,13 +1344,15 @@ export class ChatProcessor {
511
1344
  }
512
1345
  };
513
1346
  const pushChunk = (chunk) => {
514
- if (ended || stream.destroyed || stream.writableEnded)
1347
+ if (ended || stream.destroyed || stream.writableEnded || signal?.aborted)
515
1348
  return;
516
1349
  stream.write(`data: ${JSON.stringify(chunk)}\n\n`);
517
1350
  };
518
1351
  const listener = (event) => {
519
- if (ended)
1352
+ if (signal?.aborted) {
1353
+ endStream();
520
1354
  return;
1355
+ }
521
1356
  try {
522
1357
  const chunk = this.eventToChunk(event);
523
1358
  if (chunk) {
@@ -527,7 +1362,6 @@ export class ChatProcessor {
527
1362
  event.type === AgentProcessEventType.Error ||
528
1363
  event.type === AgentProcessEventType.Aborted) {
529
1364
  endStream();
530
- agentStore.removeListener(chatId, listener);
531
1365
  }
532
1366
  }
533
1367
  catch (err) {
@@ -536,21 +1370,18 @@ export class ChatProcessor {
536
1370
  error: err instanceof Error ? err.message : 'Unknown error occurred',
537
1371
  });
538
1372
  endStream(err);
539
- agentStore.removeListener(chatId, listener);
540
1373
  }
541
1374
  };
542
- // Cleanup on consumer side closing
543
1375
  const closeHandler = () => {
544
- agentStore.removeListener(chatId, listener);
1376
+ this.agentStore.removeListener(chatId, listener);
545
1377
  };
546
1378
  const errorHandler = (err) => {
547
- logger.error({ err }, 'Readable stream error');
548
- agentStore.removeListener(chatId, listener);
1379
+ signal?.removeEventListener('abort', endStream);
549
1380
  endStream(err);
550
1381
  };
551
1382
  stream.on('close', closeHandler);
552
1383
  stream.on('error', errorHandler);
553
- const listenerAttached = agentStore.addListener(chatId, listener);
1384
+ const listenerAttached = this.agentStore.addListener(chatId, listener);
554
1385
  if (!listenerAttached) {
555
1386
  // Remove stream listeners if listener attachment failed to prevent memory leak
556
1387
  stream.removeListener('close', closeHandler);
@@ -559,50 +1390,107 @@ export class ChatProcessor {
559
1390
  }
560
1391
  return stream;
561
1392
  }
562
- async createMessageStream(payload, excludeSocketId) {
563
- const stream = new PassThrough();
564
- (async () => {
565
- try {
566
- const chat = await this.upsertAndGetChat(payload, excludeSocketId);
567
- // Send chat metadata first
568
- if (!('chatId' in payload) && !('messageId' in payload) && chat) {
569
- stream.write(`data: ${JSON.stringify({
570
- type: StreamChunkType.Chat,
571
- chatId: chat.id,
572
- chat,
573
- })}\n\n`);
574
- }
575
- await this.streamMessage(chat, payload, excludeSocketId, async () => {
576
- if (stream.destroyed || stream.writableEnded) {
577
- return;
578
- }
579
- const messageStream = this.getMessageStream(chat.id);
580
- // If client disconnects, destroy inner stream
581
- stream.on('close', () => {
582
- if (!messageStream.destroyed) {
583
- messageStream.destroy();
584
- }
585
- });
586
- await pipelineAsync(messageStream, stream);
587
- });
1393
+ async upsertAndGetMultiUserChat(payload, excludeSocketId) {
1394
+ const targetUserId = payload.userId ?? 'guest';
1395
+ let chat = null;
1396
+ let isNewChat = false;
1397
+ if (payload.chatId) {
1398
+ chat = await this.chatRepository.findById(payload.chatId);
1399
+ if (!chat) {
1400
+ throw new Error('Chat not found');
588
1401
  }
589
- catch (error) {
590
- logger.error({ error }, 'createMessageStream failed');
591
- if (!stream.destroyed && !stream.writableEnded) {
592
- stream.write(`data: ${JSON.stringify({
593
- type: StreamChunkType.Error,
594
- error: error instanceof Error ? error.message : 'Unknown error occurred',
595
- })}\n\n`);
596
- stream.write('data: [DONE]\n\n');
597
- stream.end();
598
- }
1402
+ if (!this.hasChatAccess(chat, targetUserId)) {
1403
+ throw new Error('Chat does not belong to this user');
1404
+ }
1405
+ const participantIds = Array.from(new Set([
1406
+ ...this.getParticipantIds(chat),
1407
+ ...(payload.participantIds ?? []),
1408
+ targetUserId,
1409
+ ].filter(Boolean)));
1410
+ const updates = {};
1411
+ if (participantIds.length !== this.getParticipantIds(chat).length) {
1412
+ updates.participantIds = participantIds;
1413
+ }
1414
+ if ('model' in payload) {
1415
+ updates.model = payload.model || undefined;
599
1416
  }
600
- })().catch((err) => {
601
- logger.error({ err }, 'Unhandled stream task error');
602
- if (!stream.destroyed)
603
- stream.destroy(err);
1417
+ if (Object.keys(updates).length > 0) {
1418
+ chat = await this.updateChatAndEmit(chat, updates, excludeSocketId);
1419
+ }
1420
+ }
1421
+ if (!chat) {
1422
+ chat = await this.createChat(payload, excludeSocketId);
1423
+ isNewChat = true;
1424
+ }
1425
+ return { chat, isNewChat };
1426
+ }
1427
+ async postMultiUserMessage(payload, excludeSocketId) {
1428
+ const { chat } = await this.upsertAndGetMultiUserChat(payload, excludeSocketId);
1429
+ const existingMessages = await this.messageRepository.findByChatId(chat.id);
1430
+ const sender = {
1431
+ id: payload.userId ?? 'guest',
1432
+ type: 'user',
1433
+ ...(payload.senderDisplayName ? { displayName: payload.senderDisplayName } : {}),
1434
+ ...(payload.senderAvatarUrl ? { avatarUrl: payload.senderAvatarUrl } : {}),
1435
+ };
1436
+ const userMessage = await this.createMessage(chat, MessageRole.User, {
1437
+ content: payload.content,
1438
+ agentName: undefined,
1439
+ sender,
1440
+ mentions: payload.mentions ?? [],
1441
+ }, payload.attachments, excludeSocketId);
1442
+ const decision = await this.shouldRunAgentForMultiUserMessage({
1443
+ chat,
1444
+ previousMessages: existingMessages,
1445
+ userMessage,
1446
+ payload,
1447
+ });
1448
+ if (!decision.shouldRun) {
1449
+ const updatedChat = await this.updateChatAndEmit(chat, { status: AgentStatus.Finished }, excludeSocketId);
1450
+ return { chat: updatedChat, message: userMessage, agentTriggered: false };
1451
+ }
1452
+ const updatedChat = await this.updateChatAndEmit(chat, { status: AgentStatus.Streaming }, undefined);
1453
+ const abortController = this.agentStore.registerAgentProcess(chat.id);
1454
+ const limitedMessages = ContextLimiter.limitContext(existingMessages, {
1455
+ maxMessages: this.config.ai.maxContextMessages,
1456
+ keepFirstUserMessage: true,
1457
+ keepSystemMessages: true,
1458
+ });
1459
+ void this.streamAgentResponseForUserMessage({
1460
+ chat,
1461
+ payload,
1462
+ previousMessages: limitedMessages,
1463
+ userMessage,
1464
+ excludeSocketId: undefined,
1465
+ signal: abortController.signal,
1466
+ agentName: decision.agentName,
1467
+ }).catch(async (error) => {
1468
+ logger.error({ error, chatId: chat.id }, 'Multi-user background agent flow failed');
1469
+ await this.updateChatAndEmit(chat, { status: AgentStatus.Error }, undefined);
1470
+ });
1471
+ return { chat: updatedChat, message: userMessage, agentTriggered: true };
1472
+ }
1473
+ async createMessageStream(payload, excludeSocketId, signal) {
1474
+ const stream = new PassThrough();
1475
+ this.runMessageStream(stream, payload, excludeSocketId, signal)
1476
+ .catch(err => {
1477
+ logger.error({ err }, 'Stream task failed');
604
1478
  });
605
1479
  return stream;
606
1480
  }
1481
+ async runMessageStream(stream, payload, excludeSocketId, signal) {
1482
+ const chat = await this.upsertAndGetChat(payload, excludeSocketId);
1483
+ if (!('chatId' in payload) && !('messageId' in payload) && chat) {
1484
+ stream.write(`data: ${JSON.stringify({
1485
+ type: StreamChunkType.Chat,
1486
+ chatId: chat.id,
1487
+ chat,
1488
+ })}\n\n`);
1489
+ }
1490
+ await this.streamMessage(chat, payload, excludeSocketId, () => {
1491
+ const messageStream = this.getMessageStream(chat.id, signal);
1492
+ messageStream.pipe(stream);
1493
+ });
1494
+ }
607
1495
  }
608
1496
  //# sourceMappingURL=ChatProcessor.js.map