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

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
@@ -0,0 +1,760 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { ChatProcessor } from './ChatProcessor';
3
+ import { MessageRole, AgentStatus, ChatType, AgentToolCallStatus } from '@multiplayer-app/ai-agent-types';
4
+ import { ConfigStore } from '../store/ConfigStore';
5
+ import { ModelStore } from '../store/ModelStore';
6
+ import { ConfigProvider } from '../config';
7
+ import { S3Lib } from '../libs/s3';
8
+ import { z } from 'zod';
9
+ describe('ChatProcessor.streamMessage', () => {
10
+ let chatProcessor;
11
+ let mockChatRepository;
12
+ let mockMessageRepository;
13
+ let mockAgentConfigRepository;
14
+ let mockArtifactStore;
15
+ let mockAgentStore;
16
+ let mockSocketService;
17
+ let mockActivityRepository;
18
+ let configStore;
19
+ let capturedOptions;
20
+ const userId = 'test-user-id';
21
+ beforeEach(() => {
22
+ vi.clearAllMocks();
23
+ capturedOptions = undefined;
24
+ ModelStore.getInstance().setModels([
25
+ { id: 'openai/gpt-4o', provider: 'openai', label: 'GPT-4o' }
26
+ ]);
27
+ // Mock repositories
28
+ mockChatRepository = {
29
+ findById: vi.fn(),
30
+ create: vi.fn(),
31
+ update: vi.fn(),
32
+ delete: vi.fn(),
33
+ findWithMessages: vi.fn(),
34
+ };
35
+ mockMessageRepository = {
36
+ findByChatId: vi.fn().mockResolvedValue([]),
37
+ findByChatIdPaginated: vi.fn().mockResolvedValue({ messages: [], hasMore: false }),
38
+ findById: vi.fn(),
39
+ create: vi.fn(),
40
+ deleteByChatId: vi.fn(),
41
+ update: vi.fn().mockImplementation(async (id, updates) => ({
42
+ id,
43
+ chat: 'test-chat-id',
44
+ role: MessageRole.Assistant,
45
+ content: '',
46
+ createdAt: new Date().toISOString(),
47
+ ...updates
48
+ })),
49
+ };
50
+ mockAgentConfigRepository = {
51
+ findByUserIdAndAgentName: vi.fn().mockResolvedValue(null),
52
+ };
53
+ // Mock artifact store
54
+ mockArtifactStore = {
55
+ listArtifacts: vi.fn().mockReturnValue([]),
56
+ deleteArtifacts: vi.fn(),
57
+ };
58
+ // Mock agent store
59
+ mockAgentStore = {
60
+ registerAgentProcess: vi.fn().mockReturnValue({
61
+ signal: new AbortController().signal,
62
+ }),
63
+ shareAgentProcessEvent: vi.fn(),
64
+ addListener: vi.fn(),
65
+ removeListener: vi.fn(),
66
+ };
67
+ // Mock socket service
68
+ mockSocketService = {
69
+ emitMessageUpdate: vi.fn(),
70
+ emitChatUpdate: vi.fn(),
71
+ };
72
+ // Setup config store with agent config
73
+ configStore = ConfigStore.getInstance();
74
+ configStore.clear();
75
+ const agentConfig = {
76
+ name: 'test-agent',
77
+ description: 'Test agent for property verification',
78
+ defaultModel: 'openai/gpt-4o',
79
+ systemPrompt: 'You are a helpful assistant',
80
+ temperature: 0.8,
81
+ maxOutputTokens: 3000,
82
+ topP: 0.95,
83
+ topK: 50,
84
+ presencePenalty: 0.6,
85
+ frequencyPenalty: 0.4,
86
+ stopSequences: ['\n\n', '---', 'END'],
87
+ seed: 42,
88
+ tools: [],
89
+ toolChoice: 'auto',
90
+ };
91
+ // Use addAgent to bypass ConfigHelper conversion
92
+ configStore.addAgent(['test-context'], agentConfig);
93
+ const config = ConfigProvider.getInstance().getConfig();
94
+ const s3Lib = new S3Lib(config.s3);
95
+ mockActivityRepository = {
96
+ create: vi.fn().mockResolvedValue({
97
+ id: 'activity-1',
98
+ ownerId: 'test-chat-id',
99
+ groupId: 'test-chat-id',
100
+ name: 'Message processing',
101
+ tenants: { userId: 'test-user-id' },
102
+ sourceId: 'user-msg-1',
103
+ sourceType: 'AgentMessage',
104
+ metadata: {}
105
+ }),
106
+ update: vi.fn().mockResolvedValue({
107
+ id: 'activity-1',
108
+ ownerId: 'test-chat-id',
109
+ groupId: 'test-chat-id',
110
+ name: 'Message processing',
111
+ tenants: { userId: 'test-user-id' },
112
+ sourceId: 'user-msg-1',
113
+ sourceType: 'AgentMessage',
114
+ metadata: {}
115
+ }),
116
+ deleteByGroupId: vi.fn().mockResolvedValue(1),
117
+ getGroupedMetadataByParentId: vi.fn().mockResolvedValue({}),
118
+ updateMetadata: vi.fn().mockResolvedValue({
119
+ id: 'activity-1',
120
+ metadata: {}
121
+ }),
122
+ };
123
+ // Create ChatProcessor
124
+ chatProcessor = new ChatProcessor({
125
+ chatRepository: mockChatRepository,
126
+ messageRepository: mockMessageRepository,
127
+ agentConfigRepository: mockAgentConfigRepository,
128
+ artifactStore: mockArtifactStore,
129
+ s3Lib,
130
+ config,
131
+ socketService: mockSocketService,
132
+ agentStore: mockAgentStore,
133
+ activityRepository: mockActivityRepository
134
+ });
135
+ });
136
+ it('should delete chat, messages, activities, and artifacts', async () => {
137
+ mockChatRepository.delete.mockResolvedValue(true);
138
+ await chatProcessor.deleteChat('chat-1');
139
+ expect(mockChatRepository.delete).toHaveBeenCalledWith('chat-1');
140
+ expect(mockMessageRepository.deleteByChatId).toHaveBeenCalledWith('chat-1');
141
+ expect(mockActivityRepository.deleteByGroupId).toHaveBeenCalledWith('chat-1');
142
+ expect(mockArtifactStore.deleteArtifacts).toHaveBeenCalledWith('chat-1');
143
+ });
144
+ it('should throw when chat does not exist and skip cleanup', async () => {
145
+ mockChatRepository.delete.mockResolvedValue(false);
146
+ await expect(chatProcessor.deleteChat('missing-chat')).rejects.toThrow('Chat not found');
147
+ expect(mockMessageRepository.deleteByChatId).not.toHaveBeenCalled();
148
+ expect(mockActivityRepository.deleteByGroupId).not.toHaveBeenCalled();
149
+ expect(mockArtifactStore.deleteArtifacts).not.toHaveBeenCalled();
150
+ });
151
+ it('should pass all agent properties correctly to streamAssistantResponse', async () => {
152
+ const chat = {
153
+ id: 'test-chat-id',
154
+ userId,
155
+ contextKey: 'test-context',
156
+ type: ChatType.Chat,
157
+ status: AgentStatus.Processing,
158
+ createdAt: new Date().toISOString(),
159
+ updatedAt: new Date().toISOString(),
160
+ };
161
+ const payload = {
162
+ chatId: chat.id,
163
+ content: 'Hello, test message',
164
+ contextKey: 'test-context',
165
+ };
166
+ // Mock chat repository
167
+ mockChatRepository.findById.mockResolvedValue(chat);
168
+ // Mock message creation
169
+ const userMessage = {
170
+ id: 'user-msg-1',
171
+ chat: chat.id,
172
+ role: MessageRole.User,
173
+ content: payload.content,
174
+ createdAt: new Date().toISOString(),
175
+ };
176
+ const assistantMessage = {
177
+ id: 'assistant-msg-1',
178
+ chat: chat.id,
179
+ role: MessageRole.Assistant,
180
+ content: '',
181
+ createdAt: new Date().toISOString(),
182
+ agentName: 'test-agent',
183
+ };
184
+ mockMessageRepository.create
185
+ .mockResolvedValueOnce(userMessage)
186
+ .mockResolvedValueOnce(assistantMessage);
187
+ // Mock stream to prevent actual API calls
188
+ const mockStream = {
189
+ fullStream: (async function* () {
190
+ yield { type: 'finish', finishReason: 'stop', text: 'Test response' };
191
+ })(),
192
+ };
193
+ // Capture options passed to streamAssistantResponse
194
+ const aiHelper = chatProcessor.aiHelper;
195
+ vi.spyOn(aiHelper, 'streamAssistantResponse').mockImplementation(async (messages, signal, options) => {
196
+ capturedOptions = options;
197
+ return mockStream;
198
+ });
199
+ // Call streamMessage
200
+ await chatProcessor.streamMessage(chat, payload);
201
+ // Verify options were captured
202
+ expect(capturedOptions).toBeDefined();
203
+ // Verify all agent properties are present in the options
204
+ if (capturedOptions) {
205
+ expect(capturedOptions).toHaveProperty('name', 'test-agent');
206
+ expect(capturedOptions).toHaveProperty('system', 'You are a helpful assistant');
207
+ expect(capturedOptions).toHaveProperty('temperature', 0.8);
208
+ expect(capturedOptions).toHaveProperty('maxOutputTokens', 3000);
209
+ expect(capturedOptions).toHaveProperty('topP', 0.95);
210
+ expect(capturedOptions).toHaveProperty('topK', 50);
211
+ expect(capturedOptions).toHaveProperty('presencePenalty', 0.6);
212
+ expect(capturedOptions).toHaveProperty('frequencyPenalty', 0.4);
213
+ expect(capturedOptions).toHaveProperty('stopSequences', ['\n\n', '---', 'END']);
214
+ expect(capturedOptions).toHaveProperty('seed', 42);
215
+ expect(capturedOptions).toHaveProperty('toolChoice', 'auto');
216
+ expect(capturedOptions).toHaveProperty('tools');
217
+ }
218
+ });
219
+ it('should handle optional agent properties when not provided', async () => {
220
+ configStore.clear();
221
+ const minimalAgentConfig = {
222
+ name: 'minimal-agent',
223
+ description: 'Minimal agent config',
224
+ systemPrompt: 'You are helpful',
225
+ tools: [],
226
+ };
227
+ configStore.addAgent(['test-context'], minimalAgentConfig);
228
+ const chat = {
229
+ id: 'test-chat-id-2',
230
+ userId,
231
+ contextKey: 'test-context',
232
+ type: ChatType.Chat,
233
+ status: AgentStatus.Processing,
234
+ createdAt: new Date().toISOString(),
235
+ updatedAt: new Date().toISOString(),
236
+ };
237
+ const payload = {
238
+ chatId: chat.id,
239
+ content: 'Test',
240
+ contextKey: 'test-context',
241
+ };
242
+ mockChatRepository.findById.mockResolvedValue(chat);
243
+ const userMessage = {
244
+ id: 'user-msg-2',
245
+ chat: chat.id,
246
+ role: MessageRole.User,
247
+ content: payload.content,
248
+ createdAt: new Date().toISOString(),
249
+ };
250
+ const assistantMessage = {
251
+ id: 'assistant-msg-2',
252
+ chat: chat.id,
253
+ role: MessageRole.Assistant,
254
+ content: '',
255
+ createdAt: new Date().toISOString(),
256
+ agentName: 'minimal-agent',
257
+ };
258
+ mockMessageRepository.create
259
+ .mockResolvedValueOnce(userMessage)
260
+ .mockResolvedValueOnce(assistantMessage);
261
+ const mockStream = {
262
+ fullStream: (async function* () {
263
+ yield { type: 'finish', finishReason: 'stop', text: 'Response' };
264
+ })(),
265
+ };
266
+ const aiHelper = chatProcessor.aiHelper;
267
+ vi.spyOn(aiHelper, 'streamAssistantResponse').mockImplementation(async (messages, signal, options) => {
268
+ capturedOptions = options;
269
+ return mockStream;
270
+ });
271
+ await chatProcessor.streamMessage(chat, payload);
272
+ // Verify required properties are present
273
+ expect(capturedOptions).toBeDefined();
274
+ if (capturedOptions) {
275
+ expect(capturedOptions).toHaveProperty('name', 'minimal-agent');
276
+ expect(capturedOptions).toHaveProperty('system', 'You are helpful');
277
+ expect(capturedOptions).toHaveProperty('tools');
278
+ }
279
+ });
280
+ it('should pass stopWhen condition when provided', async () => {
281
+ configStore.clear();
282
+ const stopWhenFn = vi.fn(() => false);
283
+ const agentConfigWithStopWhen = {
284
+ name: 'agent-with-stopwhen',
285
+ description: 'Agent with stopWhen',
286
+ systemPrompt: 'You are helpful',
287
+ tools: [],
288
+ stopWhen: stopWhenFn,
289
+ };
290
+ configStore.addAgent(['test-context'], agentConfigWithStopWhen);
291
+ const chat = {
292
+ id: 'test-chat-id-3',
293
+ userId,
294
+ contextKey: 'test-context',
295
+ type: ChatType.Chat,
296
+ status: AgentStatus.Processing,
297
+ createdAt: new Date().toISOString(),
298
+ updatedAt: new Date().toISOString(),
299
+ };
300
+ const payload = {
301
+ chatId: chat.id,
302
+ content: 'Test',
303
+ contextKey: 'test-context',
304
+ };
305
+ mockChatRepository.findById.mockResolvedValue(chat);
306
+ const userMessage = {
307
+ id: 'user-msg-3',
308
+ chat: chat.id,
309
+ role: MessageRole.User,
310
+ content: payload.content,
311
+ createdAt: new Date().toISOString(),
312
+ };
313
+ const assistantMessage = {
314
+ id: 'assistant-msg-3',
315
+ chat: chat.id,
316
+ role: MessageRole.Assistant,
317
+ content: '',
318
+ createdAt: new Date().toISOString(),
319
+ agentName: 'agent-with-stopwhen',
320
+ };
321
+ mockMessageRepository.create
322
+ .mockResolvedValueOnce(userMessage)
323
+ .mockResolvedValueOnce(assistantMessage);
324
+ const mockStream = {
325
+ fullStream: (async function* () {
326
+ yield { type: 'finish', finishReason: 'stop', text: 'Response' };
327
+ })(),
328
+ };
329
+ const aiHelper = chatProcessor.aiHelper;
330
+ vi.spyOn(aiHelper, 'streamAssistantResponse').mockImplementation(async (messages, signal, options) => {
331
+ capturedOptions = options;
332
+ return mockStream;
333
+ });
334
+ await chatProcessor.streamMessage(chat, payload);
335
+ // Verify stopWhen is passed
336
+ expect(capturedOptions).toBeDefined();
337
+ if (capturedOptions) {
338
+ expect(capturedOptions).toHaveProperty('stopWhen', stopWhenFn);
339
+ }
340
+ });
341
+ it('should merge only cost-related provider usage into stored activity metadata', async () => {
342
+ const chat = {
343
+ id: 'test-chat-id-usage',
344
+ userId,
345
+ contextKey: 'test-context',
346
+ type: ChatType.Chat,
347
+ status: AgentStatus.Processing,
348
+ createdAt: new Date().toISOString(),
349
+ updatedAt: new Date().toISOString(),
350
+ };
351
+ await chatProcessor.storeStepActivity({
352
+ chat,
353
+ stepResult: {
354
+ finishReason: 'stop',
355
+ usage: {
356
+ inputTokens: 205,
357
+ outputTokens: 3,
358
+ totalTokens: 209,
359
+ inputTokenDetails: { cacheReadTokens: 0 },
360
+ },
361
+ response: {
362
+ timestamp: new Date().toISOString(),
363
+ modelId: 'openai/gpt-4o',
364
+ },
365
+ providerMetadata: {
366
+ openrouter: {
367
+ provider: 'OpenAI',
368
+ usage: {
369
+ promptTokens: 205,
370
+ promptTokensDetails: { cachedTokens: 0 },
371
+ completionTokens: 3,
372
+ totalTokens: 208,
373
+ cost: 0.0005425,
374
+ costDetails: {
375
+ upstreamInferenceCost: 0.0005425,
376
+ },
377
+ },
378
+ },
379
+ },
380
+ },
381
+ name: 'titleGeneration',
382
+ sourceId: chat.id,
383
+ sourceType: 'Chat',
384
+ });
385
+ expect(mockActivityRepository.create).toHaveBeenCalledTimes(1);
386
+ const [activityPayload] = mockActivityRepository.create.mock.calls[0];
387
+ expect(activityPayload.metadata.usage).toMatchObject({
388
+ inputTokens: 205,
389
+ outputTokens: 3,
390
+ totalTokens: 209,
391
+ inputTokenDetails: { cacheReadTokens: 0 },
392
+ cost: 0.0005425,
393
+ costDetails: { upstreamInferenceCost: 0.0005425 },
394
+ });
395
+ expect(activityPayload.metadata.usage.promptTokens).toBeUndefined();
396
+ expect(activityPayload.metadata.usage.completionTokens).toBeUndefined();
397
+ expect(activityPayload.metadata.usage.promptTokensDetails).toBeUndefined();
398
+ });
399
+ });
400
+ describe('ChatProcessor.getMessages', () => {
401
+ let chatProcessor;
402
+ let mockChatRepository;
403
+ let mockMessageRepository;
404
+ let mockAgentConfigRepository;
405
+ let mockActivityRepository;
406
+ beforeEach(() => {
407
+ vi.clearAllMocks();
408
+ mockChatRepository = { findById: vi.fn() };
409
+ mockMessageRepository = {
410
+ findByChatIdPaginated: vi.fn().mockResolvedValue({ messages: [], hasMore: false }),
411
+ };
412
+ mockAgentConfigRepository = {
413
+ findByUserIdAndAgentName: vi.fn().mockResolvedValue(null),
414
+ };
415
+ mockActivityRepository = {
416
+ create: vi.fn(),
417
+ update: vi.fn(),
418
+ deleteByGroupId: vi.fn(),
419
+ };
420
+ const config = ConfigProvider.getInstance().getConfig();
421
+ const s3Lib = new S3Lib(config.s3);
422
+ chatProcessor = new ChatProcessor({
423
+ chatRepository: mockChatRepository,
424
+ messageRepository: mockMessageRepository,
425
+ agentConfigRepository: mockAgentConfigRepository,
426
+ artifactStore: { listArtifacts: vi.fn().mockReturnValue([]), deleteArtifacts: vi.fn() },
427
+ s3Lib,
428
+ config,
429
+ socketService: { emitMessageUpdate: vi.fn(), emitChatUpdate: vi.fn() },
430
+ agentStore: {
431
+ registerAgentProcess: vi.fn().mockReturnValue({ signal: new AbortController().signal }),
432
+ shareAgentProcessEvent: vi.fn(),
433
+ addListener: vi.fn(),
434
+ removeListener: vi.fn(),
435
+ },
436
+ activityRepository: mockActivityRepository,
437
+ });
438
+ const aiHelper = chatProcessor.aiHelper;
439
+ vi.spyOn(aiHelper, 'getAgentOptions').mockResolvedValue({
440
+ name: 'test-agent',
441
+ tools: {
442
+ test_tool: {
443
+ inputSchema: z.object({
444
+ foo: z.any().optional(),
445
+ count: z.any().optional(),
446
+ x: z.any().optional(),
447
+ }),
448
+ },
449
+ },
450
+ });
451
+ });
452
+ it('returns messages page when chat exists', async () => {
453
+ const chatId = 'chat-1';
454
+ const messages = [
455
+ {
456
+ id: 'm1',
457
+ chat: chatId,
458
+ role: MessageRole.User,
459
+ content: 'Hi',
460
+ createdAt: new Date().toISOString(),
461
+ },
462
+ ];
463
+ mockChatRepository.findById.mockResolvedValue({ id: chatId });
464
+ mockMessageRepository.findByChatIdPaginated.mockResolvedValue({
465
+ messages,
466
+ hasMore: true,
467
+ });
468
+ const result = await chatProcessor.getMessages(chatId, { limit: 30 });
469
+ expect(result).toEqual({ messages, hasMore: true });
470
+ expect(mockMessageRepository.findByChatIdPaginated).toHaveBeenCalledWith(chatId, {
471
+ limit: 30,
472
+ before: undefined,
473
+ });
474
+ });
475
+ it('throws when chat not found', async () => {
476
+ mockChatRepository.findById.mockResolvedValue(null);
477
+ await expect(chatProcessor.getMessages('missing-chat')).rejects.toThrow('Chat not found');
478
+ expect(mockMessageRepository.findByChatIdPaginated).not.toHaveBeenCalled();
479
+ });
480
+ });
481
+ describe('ChatProcessor.updateToolCall', () => {
482
+ let chatProcessor;
483
+ let mockChatRepository;
484
+ let mockMessageRepository;
485
+ let mockAgentConfigRepository;
486
+ let mockActivityRepository;
487
+ let mockSocketService;
488
+ beforeEach(() => {
489
+ vi.clearAllMocks();
490
+ mockChatRepository = {
491
+ findById: vi.fn(),
492
+ update: vi.fn(),
493
+ };
494
+ mockMessageRepository = {
495
+ findById: vi.fn(),
496
+ updateToolCall: vi.fn(),
497
+ };
498
+ mockAgentConfigRepository = {
499
+ findByUserIdAndAgentName: vi.fn().mockResolvedValue(null),
500
+ };
501
+ mockActivityRepository = {
502
+ create: vi.fn(),
503
+ update: vi.fn(),
504
+ deleteByGroupId: vi.fn(),
505
+ };
506
+ mockSocketService = {
507
+ emitMessageUpdate: vi.fn(),
508
+ emitChatUpdate: vi.fn(),
509
+ };
510
+ const config = ConfigProvider.getInstance().getConfig();
511
+ const s3Lib = new S3Lib(config.s3);
512
+ chatProcessor = new ChatProcessor({
513
+ chatRepository: mockChatRepository,
514
+ messageRepository: mockMessageRepository,
515
+ agentConfigRepository: mockAgentConfigRepository,
516
+ artifactStore: { listArtifacts: vi.fn().mockReturnValue([]), deleteArtifacts: vi.fn() },
517
+ s3Lib,
518
+ config,
519
+ socketService: mockSocketService,
520
+ agentStore: {
521
+ registerAgentProcess: vi.fn().mockReturnValue({ signal: new AbortController().signal }),
522
+ shareAgentProcessEvent: vi.fn(),
523
+ addListener: vi.fn(),
524
+ removeListener: vi.fn(),
525
+ },
526
+ activityRepository: mockActivityRepository,
527
+ });
528
+ });
529
+ it('updates targeted tool call input and emits update', async () => {
530
+ const chatId = 'chat-1';
531
+ const messageId = 'message-1';
532
+ const toolCallId = 'tool-call-1';
533
+ const userId = 'user-1';
534
+ const updatedInput = { foo: 'bar', count: 1 };
535
+ const now = new Date().toISOString();
536
+ mockChatRepository.findById.mockResolvedValue({
537
+ id: chatId,
538
+ userId,
539
+ });
540
+ mockMessageRepository.findById.mockResolvedValue({
541
+ id: messageId,
542
+ chat: chatId,
543
+ role: MessageRole.Assistant,
544
+ content: '',
545
+ createdAt: now,
546
+ toolCalls: [
547
+ {
548
+ id: toolCallId,
549
+ name: 'test_tool',
550
+ input: { foo: 'old' },
551
+ status: AgentToolCallStatus.Pending,
552
+ },
553
+ ],
554
+ });
555
+ mockMessageRepository.updateToolCall.mockResolvedValue({
556
+ id: messageId,
557
+ chat: chatId,
558
+ role: MessageRole.Assistant,
559
+ content: '',
560
+ createdAt: now,
561
+ toolCalls: [
562
+ {
563
+ id: toolCallId,
564
+ name: 'test_tool',
565
+ input: updatedInput,
566
+ status: AgentToolCallStatus.Pending,
567
+ },
568
+ ],
569
+ });
570
+ const aiHelper = chatProcessor.aiHelper;
571
+ vi.spyOn(aiHelper, 'getAgentOptions').mockResolvedValue({
572
+ name: 'test-agent',
573
+ tools: {
574
+ test_tool: {
575
+ inputSchema: z.object({
576
+ foo: z.any().optional(),
577
+ count: z.any().optional(),
578
+ }),
579
+ },
580
+ },
581
+ });
582
+ const result = await chatProcessor.updateToolCall({
583
+ chatId,
584
+ messageId,
585
+ toolCallId,
586
+ input: updatedInput,
587
+ excludeSocketId: 'socket-1',
588
+ });
589
+ expect(mockMessageRepository.updateToolCall).toHaveBeenCalledWith(messageId, toolCallId, {
590
+ input: updatedInput,
591
+ });
592
+ expect(mockChatRepository.update).toHaveBeenCalledWith(chatId, expect.objectContaining({ updatedAt: expect.any(String) }));
593
+ expect(mockSocketService.emitMessageUpdate).toHaveBeenCalledWith(userId, result, 'socket-1');
594
+ expect(result.toolCalls?.[0].input).toEqual(updatedInput);
595
+ });
596
+ it('updates targeted tool call output and status', async () => {
597
+ const chatId = 'chat-1';
598
+ const messageId = 'message-1';
599
+ const toolCallId = 'tool-call-1';
600
+ const userId = 'user-1';
601
+ const now = new Date().toISOString();
602
+ mockChatRepository.findById.mockResolvedValue({
603
+ id: chatId,
604
+ userId,
605
+ });
606
+ mockMessageRepository.findById.mockResolvedValue({
607
+ id: messageId,
608
+ chat: chatId,
609
+ role: MessageRole.Assistant,
610
+ content: '',
611
+ createdAt: now,
612
+ toolCalls: [
613
+ {
614
+ id: toolCallId,
615
+ name: 'test_tool',
616
+ input: { foo: 'old' },
617
+ status: AgentToolCallStatus.Running,
618
+ },
619
+ ],
620
+ });
621
+ mockMessageRepository.updateToolCall.mockResolvedValue({
622
+ id: messageId,
623
+ chat: chatId,
624
+ role: MessageRole.Assistant,
625
+ content: '',
626
+ createdAt: now,
627
+ toolCalls: [
628
+ {
629
+ id: toolCallId,
630
+ name: 'test_tool',
631
+ input: { foo: 'old' },
632
+ output: { result: 'ok' },
633
+ status: AgentToolCallStatus.Succeeded,
634
+ },
635
+ ],
636
+ });
637
+ const result = await chatProcessor.updateToolCall({
638
+ chatId,
639
+ messageId,
640
+ toolCallId,
641
+ output: { result: 'ok' },
642
+ status: AgentToolCallStatus.Succeeded,
643
+ excludeSocketId: 'socket-1',
644
+ });
645
+ expect(mockMessageRepository.updateToolCall).toHaveBeenCalledWith(messageId, toolCallId, {
646
+ output: { result: 'ok' },
647
+ status: AgentToolCallStatus.Succeeded,
648
+ });
649
+ expect(mockChatRepository.update).toHaveBeenCalledWith(chatId, expect.objectContaining({ updatedAt: expect.any(String) }));
650
+ expect(mockSocketService.emitMessageUpdate).toHaveBeenCalledWith(userId, result, 'socket-1');
651
+ expect(result.toolCalls?.[0].status).toBe(AgentToolCallStatus.Succeeded);
652
+ expect(result.toolCalls?.[0].output).toEqual({ result: 'ok' });
653
+ });
654
+ it('throws when message is not in chat', async () => {
655
+ mockChatRepository.findById.mockResolvedValue({ id: 'chat-1', userId: 'user-1' });
656
+ mockMessageRepository.findById.mockResolvedValue({
657
+ id: 'message-1',
658
+ chat: 'other-chat',
659
+ role: MessageRole.Assistant,
660
+ content: '',
661
+ createdAt: new Date().toISOString(),
662
+ toolCalls: [],
663
+ });
664
+ await expect(chatProcessor.updateToolCall({
665
+ chatId: 'chat-1',
666
+ messageId: 'message-1',
667
+ toolCallId: 'tool-call-1',
668
+ input: { x: 1 },
669
+ })).rejects.toThrow('Message not found or does not belong to this chat');
670
+ expect(mockMessageRepository.updateToolCall).not.toHaveBeenCalled();
671
+ });
672
+ it('throws when chat does not belong to user', async () => {
673
+ mockChatRepository.findById.mockResolvedValue({ id: 'chat-1', userId: 'owner-1' });
674
+ await expect(chatProcessor.updateToolCall({
675
+ chatId: 'chat-1',
676
+ messageId: 'message-1',
677
+ toolCallId: 'tool-call-1',
678
+ userId: 'other-user',
679
+ input: { x: 1 },
680
+ })).rejects.toThrow('Chat does not belong to this user');
681
+ expect(mockMessageRepository.findById).not.toHaveBeenCalled();
682
+ expect(mockMessageRepository.updateToolCall).not.toHaveBeenCalled();
683
+ });
684
+ it('throws when tool call does not exist in message', async () => {
685
+ mockChatRepository.findById.mockResolvedValue({ id: 'chat-1', userId: 'user-1' });
686
+ mockMessageRepository.findById.mockResolvedValue({
687
+ id: 'message-1',
688
+ chat: 'chat-1',
689
+ role: MessageRole.Assistant,
690
+ content: '',
691
+ createdAt: new Date().toISOString(),
692
+ toolCalls: [
693
+ {
694
+ id: 'other-tool-call',
695
+ name: 'test_tool',
696
+ input: {},
697
+ status: AgentToolCallStatus.Pending,
698
+ },
699
+ ],
700
+ });
701
+ await expect(chatProcessor.updateToolCall({
702
+ chatId: 'chat-1',
703
+ messageId: 'message-1',
704
+ toolCallId: 'tool-call-1',
705
+ input: { x: 1 },
706
+ })).rejects.toThrow('Tool call not found in message');
707
+ expect(mockMessageRepository.updateToolCall).not.toHaveBeenCalled();
708
+ });
709
+ it('throws when no tool call fields are provided', async () => {
710
+ await expect(chatProcessor.updateToolCall({
711
+ chatId: 'chat-1',
712
+ messageId: 'message-1',
713
+ toolCallId: 'tool-call-1',
714
+ })).rejects.toThrow('At least one of input, output, or status must be provided');
715
+ });
716
+ it('throws when input does not match tool schema', async () => {
717
+ const chatId = 'chat-1';
718
+ const messageId = 'message-1';
719
+ const toolCallId = 'tool-call-1';
720
+ mockChatRepository.findById.mockResolvedValue({
721
+ id: chatId,
722
+ userId: 'user-1',
723
+ });
724
+ mockMessageRepository.findById.mockResolvedValue({
725
+ id: messageId,
726
+ chat: chatId,
727
+ role: MessageRole.Assistant,
728
+ content: '',
729
+ createdAt: new Date().toISOString(),
730
+ toolCalls: [
731
+ {
732
+ id: toolCallId,
733
+ name: 'test_tool',
734
+ input: {},
735
+ status: AgentToolCallStatus.Pending,
736
+ },
737
+ ],
738
+ agentName: 'test-agent',
739
+ });
740
+ const aiHelper = chatProcessor.aiHelper;
741
+ vi.spyOn(aiHelper, 'getAgentOptions').mockResolvedValue({
742
+ name: 'test-agent',
743
+ tools: {
744
+ test_tool: {
745
+ inputSchema: z.object({
746
+ x: z.number(),
747
+ }),
748
+ },
749
+ },
750
+ });
751
+ await expect(chatProcessor.updateToolCall({
752
+ chatId,
753
+ messageId,
754
+ toolCallId,
755
+ input: { x: 'invalid' },
756
+ })).rejects.toThrow('Invalid tool input for "test_tool"');
757
+ expect(mockMessageRepository.updateToolCall).not.toHaveBeenCalled();
758
+ });
759
+ });
760
+ //# sourceMappingURL=ChatProcessor.test.js.map