@multiplayer-app/ai-agent-node 0.1.0-beta.7 → 0.1.0-beta.70

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