@multiplayer-app/ai-agent-node 0.1.0-beta.5 → 0.1.0-beta.51

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 (274) 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 +57 -23
  5. package/dist/cjs/config.d.ts.map +1 -1
  6. package/dist/cjs/helpers/AIHelper.cjs +127 -65
  7. package/dist/cjs/helpers/AIHelper.cjs.map +1 -1
  8. package/dist/cjs/helpers/AIHelper.d.ts +22 -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 +120 -28
  24. package/dist/cjs/index.cjs.map +1 -1
  25. package/dist/cjs/index.d.ts +43 -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 +39 -0
  40. package/dist/cjs/processors/ActivityProcessor.cjs.map +1 -0
  41. package/dist/cjs/processors/ActivityProcessor.d.ts +32 -0
  42. package/dist/cjs/processors/ActivityProcessor.d.ts.map +1 -0
  43. package/dist/cjs/processors/ActivityProcessor.test.cjs +84 -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 +46 -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 +355 -122
  56. package/dist/cjs/processors/ChatProcessor.cjs.map +1 -1
  57. package/dist/cjs/processors/ChatProcessor.d.ts +71 -11
  58. package/dist/cjs/processors/ChatProcessor.d.ts.map +1 -1
  59. package/dist/cjs/processors/ChatProcessor.test.cjs +762 -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 +87 -21
  68. package/dist/cjs/services/AIService.cjs.map +1 -1
  69. package/dist/cjs/services/AIService.d.ts +19 -7
  70. package/dist/cjs/services/AIService.d.ts.map +1 -1
  71. package/dist/cjs/services/InternalEventsHandler.cjs +3 -3
  72. package/dist/cjs/services/InternalEventsHandler.cjs.map +1 -1
  73. package/dist/cjs/services/InternalEventsHandler.d.ts +3 -1
  74. package/dist/cjs/services/InternalEventsHandler.d.ts.map +1 -1
  75. package/dist/cjs/services/ModelFetcher.cjs +2 -8
  76. package/dist/cjs/services/ModelFetcher.cjs.map +1 -1
  77. package/dist/cjs/services/ModelFetcher.d.ts +2 -7
  78. package/dist/cjs/services/ModelFetcher.d.ts.map +1 -1
  79. package/dist/cjs/services/RedisService.cjs +20 -16
  80. package/dist/cjs/services/RedisService.cjs.map +1 -1
  81. package/dist/cjs/services/RedisService.d.ts +5 -2
  82. package/dist/cjs/services/RedisService.d.ts.map +1 -1
  83. package/dist/cjs/services/SocketService.cjs +8 -8
  84. package/dist/cjs/services/SocketService.cjs.map +1 -1
  85. package/dist/cjs/services/SocketService.d.ts +9 -6
  86. package/dist/cjs/services/SocketService.d.ts.map +1 -1
  87. package/dist/cjs/services/index.cjs +0 -1
  88. package/dist/cjs/services/index.cjs.map +1 -1
  89. package/dist/cjs/services/index.d.ts +0 -1
  90. package/dist/cjs/services/index.d.ts.map +1 -1
  91. package/dist/cjs/store/AgentStore.cjs +3 -4
  92. package/dist/cjs/store/AgentStore.cjs.map +1 -1
  93. package/dist/cjs/store/AgentStore.d.ts +2 -1
  94. package/dist/cjs/store/AgentStore.d.ts.map +1 -1
  95. package/dist/cjs/store/ConfigStore.cjs +7 -3
  96. package/dist/cjs/store/ConfigStore.cjs.map +1 -1
  97. package/dist/cjs/store/ConfigStore.d.ts +1 -0
  98. package/dist/cjs/store/ConfigStore.d.ts.map +1 -1
  99. package/dist/cjs/tools/generateChartTool.d.ts +2 -2
  100. package/dist/cjs/tools/proposeFormValuesTool.d.ts +2 -2
  101. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  102. package/dist/esm/config.d.ts +57 -23
  103. package/dist/esm/config.d.ts.map +1 -1
  104. package/dist/esm/config.js +88 -35
  105. package/dist/esm/config.js.map +1 -1
  106. package/dist/esm/helpers/AIHelper.d.ts +22 -16
  107. package/dist/esm/helpers/AIHelper.d.ts.map +1 -1
  108. package/dist/esm/helpers/AIHelper.js +134 -70
  109. package/dist/esm/helpers/AIHelper.js.map +1 -1
  110. package/dist/esm/helpers/AIHelper.test.js +22 -15
  111. package/dist/esm/helpers/AIHelper.test.js.map +1 -1
  112. package/dist/esm/helpers/ConfigHelper.d.ts.map +1 -1
  113. package/dist/esm/helpers/ConfigHelper.js +15 -6
  114. package/dist/esm/helpers/ConfigHelper.js.map +1 -1
  115. package/dist/esm/helpers/FileHelper.d.ts +19 -25
  116. package/dist/esm/helpers/FileHelper.d.ts.map +1 -1
  117. package/dist/esm/helpers/FileHelper.js +131 -146
  118. package/dist/esm/helpers/FileHelper.js.map +1 -1
  119. package/dist/esm/helpers/index.d.ts +0 -1
  120. package/dist/esm/helpers/index.d.ts.map +1 -1
  121. package/dist/esm/helpers/index.js +0 -1
  122. package/dist/esm/helpers/index.js.map +1 -1
  123. package/dist/esm/index.d.ts +43 -11
  124. package/dist/esm/index.d.ts.map +1 -1
  125. package/dist/esm/index.js +92 -11
  126. package/dist/esm/index.js.map +1 -1
  127. package/dist/esm/libs/index.d.ts +0 -1
  128. package/dist/esm/libs/index.d.ts.map +1 -1
  129. package/dist/esm/libs/index.js +0 -1
  130. package/dist/esm/libs/index.js.map +1 -1
  131. package/dist/esm/libs/s3/index.d.ts +1 -2
  132. package/dist/esm/libs/s3/index.d.ts.map +1 -1
  133. package/dist/esm/libs/s3/index.js +1 -2
  134. package/dist/esm/libs/s3/index.js.map +1 -1
  135. package/dist/esm/libs/s3/s3.lib.d.ts +29 -22
  136. package/dist/esm/libs/s3/s3.lib.d.ts.map +1 -1
  137. package/dist/esm/libs/s3/s3.lib.js +177 -172
  138. package/dist/esm/libs/s3/s3.lib.js.map +1 -1
  139. package/dist/esm/processors/ActivityProcessor.d.ts +32 -0
  140. package/dist/esm/processors/ActivityProcessor.d.ts.map +1 -0
  141. package/dist/esm/processors/ActivityProcessor.js +36 -0
  142. package/dist/esm/processors/ActivityProcessor.js.map +1 -0
  143. package/dist/esm/processors/ActivityProcessor.test.d.ts +2 -0
  144. package/dist/esm/processors/ActivityProcessor.test.d.ts.map +1 -0
  145. package/dist/esm/processors/ActivityProcessor.test.js +82 -0
  146. package/dist/esm/processors/ActivityProcessor.test.js.map +1 -0
  147. package/dist/esm/processors/AgentProcessor.d.ts +25 -0
  148. package/dist/esm/processors/AgentProcessor.d.ts.map +1 -0
  149. package/dist/esm/processors/AgentProcessor.js +43 -0
  150. package/dist/esm/processors/AgentProcessor.js.map +1 -0
  151. package/dist/esm/processors/AgentProcessor.test.d.ts +2 -0
  152. package/dist/esm/processors/AgentProcessor.test.d.ts.map +1 -0
  153. package/dist/esm/processors/AgentProcessor.test.js +101 -0
  154. package/dist/esm/processors/AgentProcessor.test.js.map +1 -0
  155. package/dist/esm/processors/ChatProcessor.d.ts +71 -11
  156. package/dist/esm/processors/ChatProcessor.d.ts.map +1 -1
  157. package/dist/esm/processors/ChatProcessor.js +366 -126
  158. package/dist/esm/processors/ChatProcessor.js.map +1 -1
  159. package/dist/esm/processors/ChatProcessor.test.d.ts +2 -0
  160. package/dist/esm/processors/ChatProcessor.test.d.ts.map +1 -0
  161. package/dist/esm/processors/ChatProcessor.test.js +760 -0
  162. package/dist/esm/processors/ChatProcessor.test.js.map +1 -0
  163. package/dist/esm/processors/index.d.ts +2 -0
  164. package/dist/esm/processors/index.d.ts.map +1 -1
  165. package/dist/esm/processors/index.js +2 -0
  166. package/dist/esm/processors/index.js.map +1 -1
  167. package/dist/esm/services/AIService.d.ts +19 -7
  168. package/dist/esm/services/AIService.d.ts.map +1 -1
  169. package/dist/esm/services/AIService.js +91 -24
  170. package/dist/esm/services/AIService.js.map +1 -1
  171. package/dist/esm/services/InternalEventsHandler.d.ts +3 -1
  172. package/dist/esm/services/InternalEventsHandler.d.ts.map +1 -1
  173. package/dist/esm/services/InternalEventsHandler.js +4 -3
  174. package/dist/esm/services/InternalEventsHandler.js.map +1 -1
  175. package/dist/esm/services/ModelFetcher.d.ts +2 -7
  176. package/dist/esm/services/ModelFetcher.d.ts.map +1 -1
  177. package/dist/esm/services/ModelFetcher.js +2 -8
  178. package/dist/esm/services/ModelFetcher.js.map +1 -1
  179. package/dist/esm/services/RedisService.d.ts +5 -2
  180. package/dist/esm/services/RedisService.d.ts.map +1 -1
  181. package/dist/esm/services/RedisService.js +21 -14
  182. package/dist/esm/services/RedisService.js.map +1 -1
  183. package/dist/esm/services/SocketService.d.ts +9 -6
  184. package/dist/esm/services/SocketService.d.ts.map +1 -1
  185. package/dist/esm/services/SocketService.js +10 -6
  186. package/dist/esm/services/SocketService.js.map +1 -1
  187. package/dist/esm/services/index.d.ts +0 -1
  188. package/dist/esm/services/index.d.ts.map +1 -1
  189. package/dist/esm/services/index.js +0 -1
  190. package/dist/esm/services/index.js.map +1 -1
  191. package/dist/esm/store/AgentStore.d.ts +2 -1
  192. package/dist/esm/store/AgentStore.d.ts.map +1 -1
  193. package/dist/esm/store/AgentStore.js +4 -2
  194. package/dist/esm/store/AgentStore.js.map +1 -1
  195. package/dist/esm/store/ConfigStore.d.ts +1 -0
  196. package/dist/esm/store/ConfigStore.d.ts.map +1 -1
  197. package/dist/esm/store/ConfigStore.js +7 -3
  198. package/dist/esm/store/ConfigStore.js.map +1 -1
  199. package/dist/esm/tools/generateChartTool.d.ts +2 -2
  200. package/dist/esm/tools/proposeFormValuesTool.d.ts +2 -2
  201. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  202. package/package.json +12 -12
  203. package/dist/cjs/helpers/SetupHelper.cjs +0 -37
  204. package/dist/cjs/helpers/SetupHelper.cjs.map +0 -1
  205. package/dist/cjs/helpers/SetupHelper.d.ts +0 -5
  206. package/dist/cjs/helpers/SetupHelper.d.ts.map +0 -1
  207. package/dist/cjs/libs/kafka/config.cjs +0 -8
  208. package/dist/cjs/libs/kafka/config.cjs.map +0 -1
  209. package/dist/cjs/libs/kafka/config.d.ts +0 -5
  210. package/dist/cjs/libs/kafka/config.d.ts.map +0 -1
  211. package/dist/cjs/libs/kafka/consumer.cjs +0 -131
  212. package/dist/cjs/libs/kafka/consumer.cjs.map +0 -1
  213. package/dist/cjs/libs/kafka/consumer.d.ts +0 -16
  214. package/dist/cjs/libs/kafka/consumer.d.ts.map +0 -1
  215. package/dist/cjs/libs/kafka/index.cjs +0 -19
  216. package/dist/cjs/libs/kafka/index.cjs.map +0 -1
  217. package/dist/cjs/libs/kafka/index.d.ts +0 -3
  218. package/dist/cjs/libs/kafka/index.d.ts.map +0 -1
  219. package/dist/cjs/libs/kafka/kafka.cjs +0 -27
  220. package/dist/cjs/libs/kafka/kafka.cjs.map +0 -1
  221. package/dist/cjs/libs/kafka/kafka.d.ts +0 -3
  222. package/dist/cjs/libs/kafka/kafka.d.ts.map +0 -1
  223. package/dist/cjs/libs/kafka/producer.cjs +0 -48
  224. package/dist/cjs/libs/kafka/producer.cjs.map +0 -1
  225. package/dist/cjs/libs/kafka/producer.d.ts +0 -11
  226. package/dist/cjs/libs/kafka/producer.d.ts.map +0 -1
  227. package/dist/cjs/libs/logger/config.cjs +0 -8
  228. package/dist/cjs/libs/logger/config.cjs.map +0 -1
  229. package/dist/cjs/libs/logger/config.d.ts +0 -5
  230. package/dist/cjs/libs/logger/config.d.ts.map +0 -1
  231. package/dist/cjs/libs/s3/config.cjs +0 -10
  232. package/dist/cjs/libs/s3/config.cjs.map +0 -1
  233. package/dist/cjs/libs/s3/config.d.ts +0 -7
  234. package/dist/cjs/libs/s3/config.d.ts.map +0 -1
  235. package/dist/cjs/services/KafkaService.cjs +0 -122
  236. package/dist/cjs/services/KafkaService.cjs.map +0 -1
  237. package/dist/cjs/services/KafkaService.d.ts +0 -35
  238. package/dist/cjs/services/KafkaService.d.ts.map +0 -1
  239. package/dist/esm/helpers/SetupHelper.d.ts +0 -5
  240. package/dist/esm/helpers/SetupHelper.d.ts.map +0 -1
  241. package/dist/esm/helpers/SetupHelper.js +0 -32
  242. package/dist/esm/helpers/SetupHelper.js.map +0 -1
  243. package/dist/esm/libs/kafka/config.d.ts +0 -5
  244. package/dist/esm/libs/kafka/config.d.ts.map +0 -1
  245. package/dist/esm/libs/kafka/config.js +0 -5
  246. package/dist/esm/libs/kafka/config.js.map +0 -1
  247. package/dist/esm/libs/kafka/consumer.d.ts +0 -16
  248. package/dist/esm/libs/kafka/consumer.d.ts.map +0 -1
  249. package/dist/esm/libs/kafka/consumer.js +0 -125
  250. package/dist/esm/libs/kafka/consumer.js.map +0 -1
  251. package/dist/esm/libs/kafka/index.d.ts +0 -3
  252. package/dist/esm/libs/kafka/index.d.ts.map +0 -1
  253. package/dist/esm/libs/kafka/index.js +0 -3
  254. package/dist/esm/libs/kafka/index.js.map +0 -1
  255. package/dist/esm/libs/kafka/kafka.d.ts +0 -3
  256. package/dist/esm/libs/kafka/kafka.d.ts.map +0 -1
  257. package/dist/esm/libs/kafka/kafka.js +0 -24
  258. package/dist/esm/libs/kafka/kafka.js.map +0 -1
  259. package/dist/esm/libs/kafka/producer.d.ts +0 -11
  260. package/dist/esm/libs/kafka/producer.d.ts.map +0 -1
  261. package/dist/esm/libs/kafka/producer.js +0 -45
  262. package/dist/esm/libs/kafka/producer.js.map +0 -1
  263. package/dist/esm/libs/logger/config.d.ts +0 -5
  264. package/dist/esm/libs/logger/config.d.ts.map +0 -1
  265. package/dist/esm/libs/logger/config.js +0 -5
  266. package/dist/esm/libs/logger/config.js.map +0 -1
  267. package/dist/esm/libs/s3/config.d.ts +0 -7
  268. package/dist/esm/libs/s3/config.d.ts.map +0 -1
  269. package/dist/esm/libs/s3/config.js +0 -7
  270. package/dist/esm/libs/s3/config.js.map +0 -1
  271. package/dist/esm/services/KafkaService.d.ts +0 -35
  272. package/dist/esm/services/KafkaService.d.ts.map +0 -1
  273. package/dist/esm/services/KafkaService.js +0 -123
  274. package/dist/esm/services/KafkaService.js.map +0 -1
@@ -1,26 +1,118 @@
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';
1
+ import { AgentStatus, AgentToolCallStatus, ChatType, MessageRole, SortOrder, StreamChunkType, ActivityOperationName, AgentToolType } from '@multiplayer-app/ai-agent-types';
2
+ import { z } from 'zod';
3
+ import { AIHelper, FileHelper } from '../helpers';
4
+ import { AgentProcessEventType } from '../store';
6
5
  import { ContextLimiter } from '../helpers';
7
- import { config } from '../config';
8
- import { PassThrough, pipeline } from 'stream';
9
- import { promisify } from 'util';
6
+ import { PassThrough } from 'stream';
10
7
  import { logger } from '../libs/logger';
11
- const pipelineAsync = promisify(pipeline);
8
+ import { ConfigStore } from '../store';
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
+ constructor(params) {
21
+ this.chatRepository = params.chatRepository;
22
+ this.messageRepository = params.messageRepository;
23
+ this.artifactStore = params.artifactStore;
24
+ this.config = params.config;
25
+ this.socketService = params.socketService;
26
+ this.agentStore = params.agentStore;
27
+ this.activityRepository = params.activityRepository;
28
+ this.agentConfigRepository = params.agentConfigRepository;
29
+ const fileHelper = new FileHelper(params.s3Lib, this.config);
30
+ this.aiHelper = new AIHelper(fileHelper, this.config);
31
+ this.debug = this.config.debug;
20
32
  }
21
33
  getTemporaryTitle(contextKey) {
22
34
  return `${contextKey} session ${new Date().toISOString()}`;
23
35
  }
36
+ isTemporaryTitle(title) {
37
+ // Matches pattern: "{contextKey} session {ISO_DATE_STRING}"
38
+ // Example: "default session 2026-01-23T10:30:45.123Z"
39
+ // toISOString() always includes milliseconds, so we require them
40
+ const pattern = /^.+ session \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
41
+ return pattern.test(title);
42
+ }
43
+ isPlainObject(value) {
44
+ return !!value && typeof value === 'object' && !Array.isArray(value);
45
+ }
46
+ mergeMissingUsageFields(target, source) {
47
+ for (const [key, sourceValue] of Object.entries(source)) {
48
+ const targetValue = target[key];
49
+ if (targetValue === undefined) {
50
+ target[key] = sourceValue;
51
+ continue;
52
+ }
53
+ if (this.isPlainObject(targetValue) && this.isPlainObject(sourceValue)) {
54
+ this.mergeMissingUsageFields(targetValue, sourceValue);
55
+ }
56
+ }
57
+ }
58
+ extractCostUsageFields(source) {
59
+ const extracted = {};
60
+ for (const [key, value] of Object.entries(source)) {
61
+ const normalizedKey = key.toLowerCase();
62
+ if (normalizedKey.includes('cost')) {
63
+ extracted[key] = value;
64
+ continue;
65
+ }
66
+ if (this.isPlainObject(value)) {
67
+ const nestedCostFields = this.extractCostUsageFields(value);
68
+ if (Object.keys(nestedCostFields).length > 0) {
69
+ extracted[key] = nestedCostFields;
70
+ }
71
+ }
72
+ }
73
+ return extracted;
74
+ }
75
+ mergeUsageWithProviderMetadata(stepUsage, providerMetadata) {
76
+ const mergedUsage = this.isPlainObject(stepUsage) ? { ...stepUsage } : {};
77
+ let hasProviderUsage = false;
78
+ if (this.isPlainObject(providerMetadata)) {
79
+ for (const providerEntry of Object.values(providerMetadata)) {
80
+ if (!this.isPlainObject(providerEntry) || !this.isPlainObject(providerEntry.usage)) {
81
+ continue;
82
+ }
83
+ const providerCostUsage = this.extractCostUsageFields(providerEntry.usage);
84
+ if (Object.keys(providerCostUsage).length === 0) {
85
+ continue;
86
+ }
87
+ hasProviderUsage = true;
88
+ this.mergeMissingUsageFields(mergedUsage, providerCostUsage);
89
+ }
90
+ }
91
+ if (this.isPlainObject(stepUsage)) {
92
+ return mergedUsage;
93
+ }
94
+ if (hasProviderUsage) {
95
+ return mergedUsage;
96
+ }
97
+ return stepUsage;
98
+ }
99
+ async validateToolCallInput(params) {
100
+ const agentOptions = await this.aiHelper.getAgentOptions({
101
+ agentName: params.agentName,
102
+ });
103
+ const tool = agentOptions.tools?.[params.toolName] ?? undefined;
104
+ const schema = tool?.inputSchema;
105
+ if (!schema) {
106
+ throw new Error(`Tool "${params.toolName}" schema not found`);
107
+ }
108
+ if (!(schema instanceof z.ZodType)) {
109
+ throw new Error(`Tool "${params.toolName}" inputSchema must be a Zod schema`);
110
+ }
111
+ const result = schema.safeParse(params.input);
112
+ if (!result.success) {
113
+ throw new Error(`Invalid tool input for "${params.toolName}": ${result.error.message}`);
114
+ }
115
+ }
24
116
  async listChats(params) {
25
117
  // Build filter object from params
26
118
  const filter = {};
@@ -30,33 +122,56 @@ export class ChatProcessor {
30
122
  if (params?.contextKey) {
31
123
  filter.contextKey = params.contextKey;
32
124
  }
33
- // Build query options for sort and limit with defaults
125
+ // Build query options for sort and limit with defaults.
34
126
  const options = {
35
127
  sort: {
36
128
  field: params?.sortField ?? 'updatedAt',
37
129
  order: params?.sortOrder ?? SortOrder.Desc
38
130
  },
131
+ skip: params?.skip,
39
132
  limit: params?.limit
40
133
  };
41
- // Use aggregation to fetch chats with related messages
42
- return this.chatRepository.findWithMessages(filter, options);
134
+ const [total, data] = await Promise.all([
135
+ this.chatRepository.count(filter),
136
+ this.chatRepository.find(filter, options)
137
+ ]);
138
+ return {
139
+ cursor: {
140
+ ...(params?.skip != null ? { skip: params.skip } : {}),
141
+ ...(params?.limit != null ? { limit: params.limit } : {}),
142
+ total
143
+ },
144
+ data
145
+ };
43
146
  }
44
147
  async getChat(chatId) {
45
148
  const chat = await this.chatRepository.findById(chatId);
46
149
  if (!chat) {
47
150
  throw new Error('Chat not found');
48
151
  }
49
- const messages = await this.messageRepository.findByChatId(chatId);
50
- return {
51
- ...chat,
52
- messages
53
- };
152
+ return chat;
153
+ }
154
+ /**
155
+ * Get messages for a chat with optional cursor pagination.
156
+ * When limit is omitted, returns all messages (backward compatible).
157
+ * Messages are returned in chronological order (oldest → newest).
158
+ */
159
+ async getMessages(chatId, options) {
160
+ const chat = await this.chatRepository.findById(chatId);
161
+ if (!chat) {
162
+ throw new Error('Chat not found');
163
+ }
164
+ return this.messageRepository.findByChatIdPaginated(chatId, options);
54
165
  }
55
166
  async deleteChat(chatId) {
56
167
  const deleted = await this.chatRepository.delete(chatId);
57
168
  if (!deleted) {
58
169
  throw new Error('Chat not found');
59
170
  }
171
+ await Promise.all([
172
+ this.messageRepository.deleteByChatId(chatId),
173
+ this.activityRepository.deleteByGroupId(chatId), // todo: discuss if this is needed
174
+ ]);
60
175
  this.artifactStore.deleteArtifacts(chatId);
61
176
  }
62
177
  async upsertAndGetChat(payload, excludeSocketId) {
@@ -92,7 +207,7 @@ export class ChatProcessor {
92
207
  if (!content) {
93
208
  return this.getTemporaryTitle(payload.contextKey);
94
209
  }
95
- return AIHelper.generateTitleForMessage(payload.contextKey, [{
210
+ return this.aiHelper.generateTitleForMessage(payload.contextKey, [{
96
211
  role: MessageRole.User,
97
212
  content: content
98
213
  }]);
@@ -108,7 +223,7 @@ export class ChatProcessor {
108
223
  toolCalls: []
109
224
  });
110
225
  if (chat.userId) {
111
- socketService.emitMessageUpdate(chat.userId, message, excludeSocketId);
226
+ this.socketService.emitMessageUpdate(chat.userId, message, excludeSocketId);
112
227
  }
113
228
  return message;
114
229
  }
@@ -125,12 +240,7 @@ export class ChatProcessor {
125
240
  metadata,
126
241
  ...(payload.model ? { model: payload.model } : {})
127
242
  });
128
- await kafkaService.sendChatTitleGenerationEvent(chat.id);
129
- const initialChatResponse = {
130
- ...chat,
131
- messages: []
132
- };
133
- socketService.emitChatUpdate(targetUserId, initialChatResponse, excludeSocketId);
243
+ this.socketService.emitChatUpdate(targetUserId, chat, excludeSocketId);
134
244
  return chat;
135
245
  }
136
246
  /**
@@ -185,33 +295,75 @@ export class ChatProcessor {
185
295
  await this.messageRepository.update(message.id, { toolCalls });
186
296
  // Bump chat updatedAt so listChats ordering reflects the action.
187
297
  await this.chatRepository.update(chat.id, { updatedAt: now });
188
- socketService.emitMessageUpdate(chat.userId, updatedMessage, params.excludeSocketId);
298
+ this.socketService.emitMessageUpdate(chat.userId, updatedMessage, params.excludeSocketId);
189
299
  if (params.systemMessage) {
190
300
  //todo discuss support for system messages
191
301
  //await this.createMessage(chat, MessageRole.System, params.systemMessage, undefined, params.excludeSocketId);
192
302
  }
193
303
  return { ok: true };
194
304
  }
305
+ /**
306
+ * Update selected fields (input/output/status) on a specific tool call.
307
+ */
308
+ async updateToolCall(params) {
309
+ if (params.input === undefined && params.output === undefined && params.status === undefined) {
310
+ throw new Error('At least one of input, output, or status must be provided');
311
+ }
312
+ const chat = await this.chatRepository.findById(params.chatId);
313
+ if (!chat) {
314
+ throw new Error('Chat not found');
315
+ }
316
+ if (params.userId && chat.userId !== params.userId) {
317
+ throw new Error('Chat does not belong to this user');
318
+ }
319
+ const message = await this.messageRepository.findById(params.messageId);
320
+ if (!message || message.chat !== params.chatId) {
321
+ throw new Error('Message not found or does not belong to this chat');
322
+ }
323
+ const targetToolCall = (message.toolCalls ?? []).find((tc) => tc.id === params.toolCallId);
324
+ if (!targetToolCall) {
325
+ throw new Error('Tool call not found in message');
326
+ }
327
+ if (params.input !== undefined) {
328
+ await this.validateToolCallInput({
329
+ agentName: message.agentName,
330
+ toolName: targetToolCall.name,
331
+ input: params.input,
332
+ });
333
+ }
334
+ const updatedMessage = await this.messageRepository.updateToolCall(params.messageId, params.toolCallId, {
335
+ ...(params.input !== undefined ? { input: params.input } : {}),
336
+ ...(params.output !== undefined ? { output: params.output } : {}),
337
+ ...(params.status !== undefined ? { status: params.status } : {}),
338
+ });
339
+ if (!updatedMessage) {
340
+ throw new Error('Tool call not found in message');
341
+ }
342
+ const now = new Date().toISOString();
343
+ await this.chatRepository.update(chat.id, { updatedAt: now });
344
+ this.socketService.emitMessageUpdate(chat.userId, updatedMessage, params.excludeSocketId);
345
+ return updatedMessage;
346
+ }
195
347
  async streamMessageStep(params) {
196
348
  const { chat, assistantMessage, existingMessages, signal, excludeSocketId, agentOptions } = params;
197
349
  if (signal.aborted) {
198
350
  return;
199
351
  }
200
- const result = await AIHelper.streamAssistantResponse(existingMessages, signal, agentOptions);
352
+ const result = await this.aiHelper.streamAssistantResponse(existingMessages, signal, agentOptions);
201
353
  try {
202
354
  for await (const chunk of result.fullStream) {
203
355
  if (chunk.type === 'error') {
204
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: chunk.error });
356
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: chunk.error });
205
357
  assistantMessage.content = chunk.error.message || 'Sorry, I cannot process you request due to the error';
206
358
  await this.messageRepository.update(assistantMessage.id, assistantMessage);
207
359
  await this.chatRepository.update(chat.id, { status: AgentStatus.Error });
208
360
  continue;
209
361
  }
210
362
  if (chunk.type === 'abort') {
211
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Aborted, data: undefined });
363
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Aborted, data: undefined });
212
364
  await this.chatRepository.update(chat.id, { status: AgentStatus.Aborted });
213
365
  await this.messageRepository.update(assistantMessage.id, assistantMessage);
214
- socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
366
+ this.socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
215
367
  continue;
216
368
  }
217
369
  if (chunk.type === 'finish') {
@@ -229,37 +381,39 @@ export class ChatProcessor {
229
381
  }
230
382
  await this.messageRepository.update(assistantMessage.id, { ...assistantMessage });
231
383
  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 });
234
- }
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 });
384
+ await this.chatRepository.update(chat.id, { status: AgentStatus.Finished });
385
+ if (chat.title && this.isTemporaryTitle(chat.title)) {
386
+ const title = await this.aiHelper.generateTitleForMessage(chat.contextKey, existingMessages.filter(m => m.role === MessageRole.User), (stepResult) => {
387
+ return this.storeStepActivity({
388
+ chat,
389
+ stepResult,
390
+ name: ActivityOperationName.TITLE_GENERATION,
391
+ sourceId: chat.id,
392
+ sourceType: 'Chat',
393
+ tenants: params.tenants || {},
394
+ });
395
+ });
396
+ await this.chatRepository.update(chat.id, { title });
397
+ this.socketService.emitChatUpdate(chat.userId, { ...chat, title });
243
398
  }
244
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
245
- continue;
246
399
  }
247
400
  if (chunk.finishReason === 'tool-calls') {
248
401
  if (assistantMessage.toolCalls?.some(toolCall => toolCall.requiresConfirmation && toolCall.approvalId && !toolCall.output)) {
249
402
  await this.chatRepository.update(chat.id, { status: AgentStatus.WaitingForUserAction });
250
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
251
- }
252
- else {
253
- await agentStore.shareAgentProcessEvent(chat.id, {
254
- type: AgentProcessEventType.Update,
255
- data: assistantMessage
256
- });
257
- await this.streamMessageStep(params);
403
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
404
+ continue;
258
405
  }
259
- continue;
260
406
  }
261
407
  //todo: Handle other finish reasons // length, content, error, other, undefined
262
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
408
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
409
+ if (assistantMessage.activity) {
410
+ const groupedMeta = await this.activityRepository.getGroupedMetadataByParentId(assistantMessage.activity);
411
+ await this.activityRepository.updateMetadata(assistantMessage.activity, {
412
+ usage: groupedMeta,
413
+ finishReason: chunk.finishReason,
414
+ responseTimestamp: new Date().toISOString(),
415
+ });
416
+ }
263
417
  continue;
264
418
  }
265
419
  if (chunk.type === 'text-delta') {
@@ -268,8 +422,8 @@ export class ChatProcessor {
268
422
  ...assistantMessage,
269
423
  content: assistantMessage.content
270
424
  };
271
- socketService.emitMessageUpdate(chat.userId, updatedMessage, excludeSocketId);
272
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: updatedMessage });
425
+ this.socketService.emitMessageUpdate(chat.userId, updatedMessage, excludeSocketId);
426
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: updatedMessage });
273
427
  await this.messageRepository.update(assistantMessage.id, { content: assistantMessage.content });
274
428
  continue;
275
429
  }
@@ -284,7 +438,7 @@ export class ChatProcessor {
284
438
  });
285
439
  continue;
286
440
  }
287
- if (chunk.type === 'tool-call') { //Todo possible place to handle user-approval
441
+ if (chunk.type === 'tool-call') {
288
442
  const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
289
443
  if (toolCall) {
290
444
  toolCall.status = AgentToolCallStatus.Running;
@@ -296,7 +450,7 @@ export class ChatProcessor {
296
450
  const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
297
451
  if (toolCall) {
298
452
  toolCall.status = AgentToolCallStatus.Failed;
299
- toolCall.error = JSON.stringify(chunk.error);
453
+ toolCall.error = chunk.error.message || JSON.stringify(chunk.error);
300
454
  }
301
455
  continue;
302
456
  }
@@ -305,13 +459,16 @@ export class ChatProcessor {
305
459
  if (toolCall) {
306
460
  toolCall.requiresConfirmation = true;
307
461
  toolCall.approvalId = chunk.approvalId;
462
+ if (toolCall.name === AgentToolType.REQUEST_CLARIFICATION) {
463
+ toolCall.requiresUserAction = true;
464
+ }
308
465
  }
309
466
  continue;
310
467
  }
311
468
  if (chunk.type === 'tool-result') {
312
469
  const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
313
470
  if (toolCall) {
314
- if (chunk.output.requiresApproval) {
471
+ if (chunk.output?.requiresApproval) {
315
472
  toolCall.requiresConfirmation = true;
316
473
  continue;
317
474
  }
@@ -324,9 +481,9 @@ export class ChatProcessor {
324
481
  if (!assistantMessage.reasoning)
325
482
  assistantMessage.reasoning = '';
326
483
  assistantMessage.reasoning += chunk.text;
327
- socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
484
+ this.socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
328
485
  await this.messageRepository.update(assistantMessage.id, { reasoning: assistantMessage.reasoning });
329
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
486
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
330
487
  continue;
331
488
  }
332
489
  }
@@ -335,7 +492,7 @@ export class ChatProcessor {
335
492
  if (streamError instanceof Error && streamError.name === 'AbortError') {
336
493
  return;
337
494
  }
338
- await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: streamError });
495
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: streamError });
339
496
  throw streamError;
340
497
  }
341
498
  }
@@ -347,7 +504,7 @@ export class ChatProcessor {
347
504
  if (!assistantMessage || assistantMessage.chat !== chat.id) {
348
505
  throw new Error(`Assistant message with id ${payload.messageId} not found`);
349
506
  }
350
- const agentOptions = await AIHelper.getAgentOptions({
507
+ const agentOptions = await this.aiHelper.getAgentOptions({
351
508
  agentName: assistantMessage.agentName,
352
509
  context: payload.context
353
510
  });
@@ -360,14 +517,20 @@ export class ChatProcessor {
360
517
  };
361
518
  }
362
519
  toolCall.approved = payload.approved;
363
- toolCall.reason = payload.reason;
520
+ toolCall.userResponse = payload.userResponse;
364
521
  if (!toolCall.approved) {
365
522
  toolCall.output = {
366
523
  type: 'execution-denied',
367
- reason: toolCall.reason
524
+ reason: toolCall.userResponse
368
525
  };
369
526
  toolCall.status = AgentToolCallStatus.Failed;
370
527
  }
528
+ else if (toolCall.requiresUserAction) {
529
+ toolCall.output = {
530
+ userResponse: toolCall.userResponse
531
+ };
532
+ toolCall.status = AgentToolCallStatus.Succeeded;
533
+ }
371
534
  else {
372
535
  const executeFunction = agentOptions.tools?.[toolCall.name]?.execute;
373
536
  if (executeFunction) {
@@ -400,32 +563,105 @@ export class ChatProcessor {
400
563
  }
401
564
  async processNewUserMessage(chat, payload, prevMessages, excludeSocketId) {
402
565
  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({
566
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: userMessage });
567
+ let assistantMessage = await this.createMessage(chat, MessageRole.Assistant, { content: '', agentName: undefined }, undefined, excludeSocketId);
568
+ const parentActivity = await this.activityRepository.create({
569
+ ownerId: chat.userId,
570
+ groupId: chat.id,
571
+ name: ActivityOperationName.MESSAGE_PROCESSING,
572
+ tenants: payload.tenants ?? {},
573
+ sourceId: userMessage.id,
574
+ sourceType: 'AgentMessage',
575
+ metadata: {
576
+ contextKey: chat.contextKey,
577
+ modelId: this.aiHelper.getLanguageModelId(chat.model),
578
+ },
579
+ });
580
+ const agentOptions = await this.aiHelper.getAgentOptions({
405
581
  contextKey: chat.contextKey,
406
- messages: await AIHelper.convertMessages([...prevMessages, userMessage], true),
582
+ messages: [...prevMessages, userMessage],
407
583
  context: payload.context,
408
584
  agentName: undefined,
409
585
  modelId: chat.model,
586
+ }, {
587
+ onStepFinish: async (stepResult) => {
588
+ await this.storeStepActivity({
589
+ chat,
590
+ parentId: parentActivity.id,
591
+ stepResult,
592
+ name: ActivityOperationName.AGENT_SELECTION,
593
+ sourceId: userMessage.id,
594
+ sourceType: 'AgentMessage',
595
+ tenants: payload.tenants || {},
596
+ });
597
+ }
598
+ });
599
+ const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
600
+ agentName: agentOptions.name,
601
+ activity: parentActivity.id
410
602
  });
411
- const assistantMessage = await this.createMessage(chat, MessageRole.Assistant, { content: ' ', agentName: agentOptions.name }, undefined, excludeSocketId);
412
- assistantMessage.content = '';
603
+ await this.activityRepository.update(parentActivity.id, {
604
+ metadata: {
605
+ ...parentActivity.metadata,
606
+ agentOptions: {
607
+ name: agentOptions.name,
608
+ modelId: this.aiHelper.getLanguageModelId(agentOptions.model),
609
+ temperature: agentOptions.temperature,
610
+ maxOutputTokens: agentOptions.maxOutputTokens,
611
+ topP: agentOptions.topP,
612
+ topK: agentOptions.topK,
613
+ presencePenalty: agentOptions.presencePenalty,
614
+ frequencyPenalty: agentOptions.frequencyPenalty,
615
+ stopSequences: agentOptions.stopSequences,
616
+ seed: agentOptions.seed,
617
+ },
618
+ },
619
+ });
620
+ if (!updatedAssistantMessage) {
621
+ throw new Error(`Assistant message with id ${assistantMessage.id} not found after update`);
622
+ }
623
+ assistantMessage = updatedAssistantMessage;
413
624
  return {
414
625
  userMessage,
415
626
  assistantMessage,
416
627
  agentOptions,
417
628
  };
418
629
  }
630
+ async storeStepActivity(params) {
631
+ try {
632
+ const stepResult = params.stepResult;
633
+ const mergedUsage = this.mergeUsageWithProviderMetadata(stepResult.usage, stepResult.providerMetadata);
634
+ const activity = await this.activityRepository.create({
635
+ ownerId: params.chat.userId,
636
+ groupId: params.chat.id,
637
+ name: params.name,
638
+ tenants: params.tenants || {},
639
+ sourceId: params.sourceId,
640
+ sourceType: params.sourceType,
641
+ metadata: {
642
+ finishReason: stepResult.finishReason,
643
+ usage: mergedUsage,
644
+ responseTimestamp: stepResult.response.timestamp,
645
+ modelId: stepResult.response.modelId,
646
+ messages: this.debug ? stepResult.request?.body?.messages : undefined,
647
+ },
648
+ parentId: params.parentId,
649
+ });
650
+ }
651
+ catch (error) {
652
+ logger.error({ error, chatId: params.chat.id, parentId: params.parentId }, 'Failed to store step activity');
653
+ }
654
+ }
419
655
  async streamMessage(chat, payload, excludeSocketId, onAgentStart) {
420
656
  try {
421
- const abortController = agentStore.registerAgentProcess(chat.id);
657
+ const abortController = this.agentStore.registerAgentProcess(chat.id);
422
658
  onAgentStart?.();
423
659
  let assistantMessage;
424
660
  let agentOptions;
425
661
  const existingMessages = await this.messageRepository.findByChatId(chat.id);
426
662
  // Limit context to prevent exceeding token limits
427
663
  const limitedMessages = ContextLimiter.limitContext(existingMessages, {
428
- maxMessages: config.ai.maxContextMessages,
664
+ maxMessages: this.config.ai.maxContextMessages,
429
665
  keepFirstUserMessage: true,
430
666
  keepSystemMessages: true,
431
667
  });
@@ -447,6 +683,19 @@ export class ChatProcessor {
447
683
  agentOptions = result.agentOptions;
448
684
  limitedMessages.push(result.userMessage);
449
685
  }
686
+ agentOptions.onStepFinish = (stepResult) => {
687
+ return this.storeStepActivity({
688
+ chat,
689
+ parentId: assistantMessage.activity,
690
+ stepResult,
691
+ name: ActivityOperationName.STEP_FINISHED,
692
+ sourceId: assistantMessage.id,
693
+ sourceType: 'AgentMessage',
694
+ tenants: payload.tenants || {},
695
+ });
696
+ };
697
+ const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(chat.userId, agentOptions.name);
698
+ agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
450
699
  await this.streamMessageStep({
451
700
  chat,
452
701
  existingMessages: limitedMessages,
@@ -454,12 +703,28 @@ export class ChatProcessor {
454
703
  agentOptions,
455
704
  excludeSocketId: excludeSocketId,
456
705
  signal: abortController.signal,
706
+ tenants: payload.tenants || {},
457
707
  });
458
708
  }
459
709
  catch (error) {
460
710
  throw error;
461
711
  }
462
712
  }
713
+ getAvailableTools(agentOptions, userPreferences) {
714
+ const toolNames = Object.keys(agentOptions.tools || {});
715
+ const availableToolsMap = Object.fromEntries(toolNames.map(key => [key, true]));
716
+ const agentsConfiguration = ConfigStore.getInstance().getAgentConfigByName(agentOptions.name);
717
+ agentsConfiguration.tools.forEach((tool) => {
718
+ const toolTitle = tool.data?.title || tool.type;
719
+ if (tool.disabledByDefault) {
720
+ availableToolsMap[toolTitle] = false;
721
+ }
722
+ if (tool.userConfigurable && userPreferences?.tools?.[toolTitle] !== undefined) {
723
+ availableToolsMap[toolTitle] = userPreferences.tools[toolTitle];
724
+ }
725
+ });
726
+ return Object.keys(availableToolsMap).filter(key => availableToolsMap[key]);
727
+ }
463
728
  listArtifacts(chatId) {
464
729
  return this.artifactStore.listArtifacts(chatId);
465
730
  }
@@ -490,7 +755,7 @@ export class ChatProcessor {
490
755
  return null;
491
756
  }
492
757
  }
493
- getMessageStream(chatId) {
758
+ getMessageStream(chatId, signal) {
494
759
  const stream = new PassThrough();
495
760
  let ended = false;
496
761
  const endStream = (error) => {
@@ -511,13 +776,15 @@ export class ChatProcessor {
511
776
  }
512
777
  };
513
778
  const pushChunk = (chunk) => {
514
- if (ended || stream.destroyed || stream.writableEnded)
779
+ if (ended || stream.destroyed || stream.writableEnded || signal?.aborted)
515
780
  return;
516
781
  stream.write(`data: ${JSON.stringify(chunk)}\n\n`);
517
782
  };
518
783
  const listener = (event) => {
519
- if (ended)
784
+ if (signal?.aborted) {
785
+ endStream();
520
786
  return;
787
+ }
521
788
  try {
522
789
  const chunk = this.eventToChunk(event);
523
790
  if (chunk) {
@@ -527,7 +794,6 @@ export class ChatProcessor {
527
794
  event.type === AgentProcessEventType.Error ||
528
795
  event.type === AgentProcessEventType.Aborted) {
529
796
  endStream();
530
- agentStore.removeListener(chatId, listener);
531
797
  }
532
798
  }
533
799
  catch (err) {
@@ -536,21 +802,18 @@ export class ChatProcessor {
536
802
  error: err instanceof Error ? err.message : 'Unknown error occurred',
537
803
  });
538
804
  endStream(err);
539
- agentStore.removeListener(chatId, listener);
540
805
  }
541
806
  };
542
- // Cleanup on consumer side closing
543
807
  const closeHandler = () => {
544
- agentStore.removeListener(chatId, listener);
808
+ this.agentStore.removeListener(chatId, listener);
545
809
  };
546
810
  const errorHandler = (err) => {
547
- logger.error({ err }, 'Readable stream error');
548
- agentStore.removeListener(chatId, listener);
811
+ signal?.removeEventListener('abort', endStream);
549
812
  endStream(err);
550
813
  };
551
814
  stream.on('close', closeHandler);
552
815
  stream.on('error', errorHandler);
553
- const listenerAttached = agentStore.addListener(chatId, listener);
816
+ const listenerAttached = this.agentStore.addListener(chatId, listener);
554
817
  if (!listenerAttached) {
555
818
  // Remove stream listeners if listener attachment failed to prevent memory leak
556
819
  stream.removeListener('close', closeHandler);
@@ -559,50 +822,27 @@ export class ChatProcessor {
559
822
  }
560
823
  return stream;
561
824
  }
562
- async createMessageStream(payload, excludeSocketId) {
825
+ async createMessageStream(payload, excludeSocketId, signal) {
563
826
  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
- });
588
- }
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
- }
599
- }
600
- })().catch((err) => {
601
- logger.error({ err }, 'Unhandled stream task error');
602
- if (!stream.destroyed)
603
- stream.destroy(err);
827
+ this.runMessageStream(stream, payload, excludeSocketId, signal)
828
+ .catch(err => {
829
+ logger.error({ err }, 'Stream task failed');
604
830
  });
605
831
  return stream;
606
832
  }
833
+ async runMessageStream(stream, payload, excludeSocketId, signal) {
834
+ const chat = await this.upsertAndGetChat(payload, excludeSocketId);
835
+ if (!('chatId' in payload) && !('messageId' in payload) && chat) {
836
+ stream.write(`data: ${JSON.stringify({
837
+ type: StreamChunkType.Chat,
838
+ chatId: chat.id,
839
+ chat,
840
+ })}\n\n`);
841
+ }
842
+ await this.streamMessage(chat, payload, excludeSocketId, () => {
843
+ const messageStream = this.getMessageStream(chat.id, signal);
844
+ messageStream.pipe(stream);
845
+ });
846
+ }
607
847
  }
608
848
  //# sourceMappingURL=ChatProcessor.js.map