@xalia/agent 0.6.10 → 0.6.11

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 (161) hide show
  1. package/package.json +5 -2
  2. package/.env.development +0 -6
  3. package/.env.test +0 -7
  4. package/.prettierrc.json +0 -11
  5. package/context_system.md +0 -498
  6. package/eslint.config.mjs +0 -38
  7. package/scripts/chat_server +0 -8
  8. package/scripts/git_message +0 -31
  9. package/scripts/git_wip +0 -21
  10. package/scripts/pr_message +0 -18
  11. package/scripts/pr_review +0 -16
  12. package/scripts/setup_chat +0 -90
  13. package/scripts/shutdown_chat_server +0 -42
  14. package/scripts/start_chat_server +0 -24
  15. package/scripts/sudomcp_import +0 -23
  16. package/scripts/test_chat +0 -327
  17. package/src/agent/agent.ts +0 -699
  18. package/src/agent/agentUtils.ts +0 -286
  19. package/src/agent/compressingContextManager.ts +0 -129
  20. package/src/agent/context.ts +0 -265
  21. package/src/agent/contextWithWorkspace.ts +0 -162
  22. package/src/agent/documentSummarizer.ts +0 -157
  23. package/src/agent/dummyLLM.ts +0 -130
  24. package/src/agent/iAgentEventHandler.ts +0 -64
  25. package/src/agent/imageGenLLM.ts +0 -101
  26. package/src/agent/imageGenerator.ts +0 -45
  27. package/src/agent/iplatform.ts +0 -18
  28. package/src/agent/llm.ts +0 -74
  29. package/src/agent/mcpServerManager.ts +0 -541
  30. package/src/agent/nullAgentEventHandler.ts +0 -26
  31. package/src/agent/nullPlatform.ts +0 -13
  32. package/src/agent/openAI.ts +0 -123
  33. package/src/agent/openAILLM.ts +0 -99
  34. package/src/agent/openAILLMStreaming.ts +0 -648
  35. package/src/agent/promptProvider.ts +0 -87
  36. package/src/agent/repeatLLM.ts +0 -62
  37. package/src/agent/sudoMcpServerManager.ts +0 -361
  38. package/src/agent/test_data/harrypotter.txt +0 -6065
  39. package/src/agent/tokenAuth.ts +0 -50
  40. package/src/agent/tokenCounter.test.ts +0 -243
  41. package/src/agent/tokenCounter.ts +0 -483
  42. package/src/agent/toolSettings.ts +0 -24
  43. package/src/agent/tools/calculatorTool.ts +0 -50
  44. package/src/agent/tools/contentExtractors/htmlToText.ts +0 -61
  45. package/src/agent/tools/contentExtractors/pdfToText.ts +0 -60
  46. package/src/agent/tools/datetimeTool.ts +0 -41
  47. package/src/agent/tools/fileManager/fileManagerTool.ts +0 -199
  48. package/src/agent/tools/fileManager/index.ts +0 -50
  49. package/src/agent/tools/fileManager/memoryFileManager.ts +0 -120
  50. package/src/agent/tools/fileManager/mimeTypes.ts +0 -60
  51. package/src/agent/tools/fileManager/prompt.ts +0 -38
  52. package/src/agent/tools/fileManager/types.ts +0 -189
  53. package/src/agent/tools/index.ts +0 -49
  54. package/src/agent/tools/openUrlTool.ts +0 -62
  55. package/src/agent/tools/renderTool.ts +0 -92
  56. package/src/agent/tools/utils.ts +0 -74
  57. package/src/agent/tools/webSearch.ts +0 -138
  58. package/src/agent/tools/webSearchTool.ts +0 -44
  59. package/src/chat/client/chatClient.ts +0 -967
  60. package/src/chat/client/connection.test.ts +0 -241
  61. package/src/chat/client/connection.ts +0 -286
  62. package/src/chat/client/constants.ts +0 -1
  63. package/src/chat/client/index.ts +0 -21
  64. package/src/chat/client/interfaces.ts +0 -34
  65. package/src/chat/client/sessionClient.ts +0 -574
  66. package/src/chat/client/sessionFiles.ts +0 -142
  67. package/src/chat/client/teamManager.ts +0 -29
  68. package/src/chat/constants.ts +0 -6
  69. package/src/chat/data/apiKeyManager.ts +0 -76
  70. package/src/chat/data/dataModels.ts +0 -107
  71. package/src/chat/data/database.ts +0 -997
  72. package/src/chat/data/dbMcpServerConfigs.ts +0 -59
  73. package/src/chat/data/dbSessionFiles.ts +0 -107
  74. package/src/chat/data/dbSessionMessages.ts +0 -102
  75. package/src/chat/protocol/connectionMessages.ts +0 -49
  76. package/src/chat/protocol/constants.ts +0 -55
  77. package/src/chat/protocol/errors.ts +0 -16
  78. package/src/chat/protocol/messages.ts +0 -899
  79. package/src/chat/server/README.md +0 -127
  80. package/src/chat/server/chatContextManager.ts +0 -660
  81. package/src/chat/server/connectionManager.test.ts +0 -246
  82. package/src/chat/server/connectionManager.ts +0 -506
  83. package/src/chat/server/conversation.ts +0 -319
  84. package/src/chat/server/errorUtils.ts +0 -28
  85. package/src/chat/server/imageGeneratorTools.ts +0 -179
  86. package/src/chat/server/openAIRouterLLM.ts +0 -168
  87. package/src/chat/server/openSession.ts +0 -1945
  88. package/src/chat/server/openSessionMessageSender.ts +0 -4
  89. package/src/chat/server/promptRefiner.ts +0 -106
  90. package/src/chat/server/server.ts +0 -178
  91. package/src/chat/server/sessionFileManager.ts +0 -151
  92. package/src/chat/server/sessionRegistry.test.ts +0 -137
  93. package/src/chat/server/sessionRegistry.ts +0 -1553
  94. package/src/chat/server/test-utils/mockFactories.ts +0 -422
  95. package/src/chat/server/titleGenerator.test.ts +0 -103
  96. package/src/chat/server/titleGenerator.ts +0 -143
  97. package/src/chat/server/tools.ts +0 -170
  98. package/src/chat/utils/agentSessionMap.ts +0 -76
  99. package/src/chat/utils/approvalManager.ts +0 -189
  100. package/src/chat/utils/asyncLock.ts +0 -43
  101. package/src/chat/utils/asyncQueue.ts +0 -62
  102. package/src/chat/utils/multiAsyncQueue.ts +0 -66
  103. package/src/chat/utils/responseAwaiter.ts +0 -181
  104. package/src/chat/utils/userResolver.ts +0 -48
  105. package/src/chat/utils/websocket.ts +0 -16
  106. package/src/index.ts +0 -0
  107. package/src/test/agent.test.ts +0 -584
  108. package/src/test/approvalManager.test.ts +0 -141
  109. package/src/test/chatContextManager.test.ts +0 -552
  110. package/src/test/clientServerConnection.test.ts +0 -205
  111. package/src/test/compressingContextManager.test.ts +0 -77
  112. package/src/test/context.test.ts +0 -150
  113. package/src/test/contextTestTools.ts +0 -95
  114. package/src/test/conversation.test.ts +0 -109
  115. package/src/test/db.test.ts +0 -363
  116. package/src/test/dbMcpServerConfigs.test.ts +0 -112
  117. package/src/test/dbSessionFiles.test.ts +0 -258
  118. package/src/test/dbSessionMessages.test.ts +0 -85
  119. package/src/test/dbTestTools.ts +0 -157
  120. package/src/test/imageLoad.test.ts +0 -15
  121. package/src/test/mcpServerManager.test.ts +0 -114
  122. package/src/test/multiAsyncQueue.test.ts +0 -183
  123. package/src/test/openaiStreaming.test.ts +0 -177
  124. package/src/test/prompt.test.ts +0 -27
  125. package/src/test/promptProvider.test.ts +0 -33
  126. package/src/test/responseAwaiter.test.ts +0 -103
  127. package/src/test/sudoMcpServerManager.test.ts +0 -63
  128. package/src/test/testTools.ts +0 -176
  129. package/src/test/tools.test.ts +0 -64
  130. package/src/tool/agentChat.ts +0 -203
  131. package/src/tool/agentMain.ts +0 -180
  132. package/src/tool/chatMain.ts +0 -621
  133. package/src/tool/commandPrompt.ts +0 -264
  134. package/src/tool/files.ts +0 -82
  135. package/src/tool/main.ts +0 -25
  136. package/src/tool/nodePlatform.ts +0 -73
  137. package/src/tool/options.ts +0 -144
  138. package/src/tool/prompt.ts +0 -101
  139. package/test_data/background_test_profile.json +0 -6
  140. package/test_data/background_test_script.json +0 -11
  141. package/test_data/dummyllm_script_crash.json +0 -32
  142. package/test_data/dummyllm_script_image_gen.json +0 -19
  143. package/test_data/dummyllm_script_image_gen_fe.json +0 -29
  144. package/test_data/dummyllm_script_invoke_image_gen_tool.json +0 -37
  145. package/test_data/dummyllm_script_render_tool.json +0 -29
  146. package/test_data/dummyllm_script_simplecalc.json +0 -28
  147. package/test_data/dummyllm_script_test_auto_approve.json +0 -81
  148. package/test_data/dummyllm_script_test_simplecalc_addition.json +0 -29
  149. package/test_data/frog.png +0 -0
  150. package/test_data/frog.png.b64 +0 -1
  151. package/test_data/git_message_profile.json +0 -4
  152. package/test_data/git_wip_system.txt +0 -5
  153. package/test_data/image_gen_test_profile.json +0 -5
  154. package/test_data/pr_message_profile.json +0 -4
  155. package/test_data/pr_review_profile.json +0 -4
  156. package/test_data/prompt_simplecalc.txt +0 -1
  157. package/test_data/simplecalc_profile.json +0 -4
  158. package/test_data/sudomcp_import_profile.json +0 -4
  159. package/test_data/test_script_profile.json +0 -8
  160. package/tsconfig.json +0 -13
  161. package/vitest.config.ts +0 -39
@@ -1,506 +0,0 @@
1
- import * as ws from "ws";
2
- import { v4 as uuidv4 } from "uuid";
3
- import { getLogger } from "@xalia/xmcp/sdk";
4
-
5
- import {
6
- ServerConnectionReady,
7
- ServerConnectionError,
8
- ServerConnectionPing,
9
- ClientConnectionMessage,
10
- ServerConnectionMessage,
11
- } from "../protocol/connectionMessages";
12
- import { ChatErrorMessage, ChatFatalError } from "../protocol/errors";
13
-
14
- const logger = getLogger();
15
-
16
- const HEARTBEAT_INTERVAL_MS: number = parseInt(
17
- process.env["HEARTBEAT_INTERVAL_MS"] || String(30 * 1000)
18
- );
19
-
20
- /**
21
- * Interface for sending messages to clients.
22
- */
23
- export interface IUserConnectionManager<ServerMsgT> {
24
- /**
25
- * Send message to all active connections of specific users.
26
- * Handles user-to-connection routing internally.
27
- */
28
- sendToUsers(userIds: Set<string> | string[], message: ServerMsgT): void;
29
-
30
- /**
31
- * Send message to a specific connection.
32
- */
33
- sendToConnection(connectionId: string, message: ServerMsgT): void;
34
- }
35
-
36
- /**
37
- * Interface for processing messages from the client. Also handles guest
38
- * admission.
39
- */
40
- export interface IMessageProcessor<ClientMsgT> {
41
- authenticate(token: string): Promise<string | undefined>;
42
-
43
- processMessage(
44
- connectionId: string,
45
- userId: string,
46
- message: ClientMsgT
47
- ): Promise<void>;
48
-
49
- /**
50
- * Handle user disconnect - clean up from all sessions.
51
- * Called when a connection is closed to ensure proper cleanup.
52
- */
53
- handleUserDisconnect(userId: string): Promise<void>;
54
- }
55
-
56
- class Connection {
57
- public ws: ws.WebSocket;
58
- public userId: string;
59
- public heartbeat: NodeJS.Timeout;
60
- public lastMessageTime: number;
61
-
62
- constructor(ws: ws.WebSocket, userId: string, heartbeat: NodeJS.Timeout) {
63
- this.ws = ws;
64
- this.userId = userId;
65
- this.heartbeat = heartbeat;
66
- this.lastMessageTime = Date.now();
67
- }
68
- }
69
-
70
- /**
71
- * Manages WebSocket connections and handles protocol-level message routing.
72
- *
73
- * - Connection handshake
74
- * - Connection lifecycle management
75
- * - Message routing between connections and sessions
76
- * - Connection-to-user
77
- */
78
- export class ConnectionManager<ClientMsgT, ServerMsgT>
79
- implements IUserConnectionManager<ServerMsgT>
80
- {
81
- // Connections by id
82
- private connections: Map<string, Connection> = new Map();
83
- // userId -> connectionIds
84
- private userToConnections: Map<string, Set<string>> = new Map();
85
-
86
- // this is a singleton object and can only be initialized by .init method
87
- private constructor(
88
- private sessionMessageProcessor: IMessageProcessor<ClientMsgT>
89
- ) {}
90
-
91
- static init<ClientMsgT, ServerMsgT>(
92
- createMsgProcessor: (
93
- cm: IUserConnectionManager<ServerMsgT>
94
- ) => IMessageProcessor<ClientMsgT>
95
- ): ConnectionManager<ClientMsgT, ServerMsgT> {
96
- const connManager = new ConnectionManager(
97
- {} as IMessageProcessor<ClientMsgT>
98
- );
99
- connManager.sessionMessageProcessor = createMsgProcessor(connManager);
100
- return connManager;
101
- }
102
-
103
- /**
104
- * Handle new WebSocket connection with handshake protocol
105
- * TODO: proper error code returning
106
- */
107
- async handleConnection(
108
- webSocket: ws.WebSocket,
109
- token: string,
110
- req?: { headers: { [key: string]: string | string[] | undefined } }
111
- ): Promise<void> {
112
- const connectionId = uuidv4();
113
- logger.info(`[ConnectionManager] New connection: ${connectionId}`);
114
-
115
- try {
116
- const userUUID = await this.sessionMessageProcessor.authenticate(token);
117
- if (!userUUID) {
118
- throw new ChatFatalError("invalid api key");
119
- }
120
-
121
- // Heartbeat interval
122
- const heartbeat = setInterval(
123
- this.onHeartbeat.bind(this, connectionId),
124
- HEARTBEAT_INTERVAL_MS
125
- );
126
- // Register connection
127
- this.connections.set(
128
- connectionId,
129
- new Connection(webSocket, userUUID, heartbeat)
130
- );
131
-
132
- // Add to user connections
133
- if (!this.userToConnections.has(userUUID)) {
134
- this.userToConnections.set(userUUID, new Set());
135
- }
136
- const userConnections = this.userToConnections.get(userUUID);
137
- if (userConnections) {
138
- userConnections.add(connectionId);
139
- }
140
-
141
- // Setup connection handlers
142
- this.setupConnectionHandlers(connectionId, webSocket);
143
-
144
- // Send connection_ready after WebSocket is fully open
145
- const clientVersion =
146
- (req?.headers["x-client-version"] as string) || "unknown";
147
-
148
- const response: ServerConnectionReady = {
149
- t: "ready",
150
- c_id: connectionId,
151
- user_uuid: userUUID,
152
- };
153
-
154
- // Check if connection still exists before sending
155
- if (this.connections.has(connectionId)) {
156
- this.sendConnectionReadyMessage(connectionId, response);
157
- } else {
158
- logger.warn(
159
- `[ConnectionManager] Connection ${connectionId} was closed` +
160
- ` before ready message could be sent`
161
- );
162
- }
163
-
164
- logger.info(
165
- `[ConnectionManager] Connection ${connectionId} registered ` +
166
- `for user ${userUUID}, version: ${clientVersion}`
167
- );
168
- } catch (error) {
169
- const errorMessage =
170
- error instanceof Error ? error.message : String(error);
171
- logger.error(
172
- `[ConnectionManager] Failed to setup connection ${connectionId}:`,
173
- errorMessage
174
- );
175
-
176
- // Send error to client if possible
177
- this.sendErrorDirect(webSocket, connectionId, errorMessage);
178
-
179
- // Force cleanup of any partial state
180
- this.forceCleanupConnection(connectionId);
181
-
182
- // Close WebSocket - this might trigger handlers if they were attached,
183
- // but we've already cleaned up state above as a safeguard
184
- try {
185
- webSocket.close(4000, "Connection setup failed");
186
- } catch (closeError) {
187
- logger.error(
188
- `[ConnectionManager] Error closing WebSocket ${connectionId}:`,
189
- closeError
190
- );
191
- }
192
- }
193
- }
194
-
195
- /**
196
- * Send connection_ready message with unified error handling
197
- */
198
- private sendConnectionReadyMessage(
199
- connectionId: string,
200
- response: ServerConnectionReady
201
- ): void {
202
- try {
203
- this.sendConnectionMessage(connectionId, response);
204
- } catch (error) {
205
- logger.error(
206
- `[ConnectionManager] Failed connection_ready to ${connectionId}:`,
207
- error
208
- );
209
- this.forceCleanupConnection(connectionId);
210
- }
211
- }
212
-
213
- /**
214
- * Send error message to connection directly.
215
- * This is used when the connection is possibly not yet registered.
216
- */
217
- private sendErrorDirect(
218
- ws: ws.WebSocket,
219
- connectionId: string,
220
- errorMessage: string
221
- ): void {
222
- const errorMsg: ServerConnectionError = {
223
- t: "error",
224
- e: errorMessage,
225
- };
226
- try {
227
- if (ws.readyState === ws.OPEN) {
228
- ws.send(JSON.stringify(errorMsg));
229
- } else {
230
- logger.error(
231
- `[ConnectionManager] Conn not open sending error ${connectionId}`
232
- );
233
- }
234
- } catch (error) {
235
- logger.error(
236
- `[ConnectionManager] Failed to send error to connection` +
237
- ` ${connectionId}:`,
238
- error
239
- );
240
- }
241
- }
242
-
243
- /**
244
- * Setup message and lifecycle handlers for a connection
245
- */
246
- private setupConnectionHandlers(
247
- connectionId: string,
248
- webSocket: ws.WebSocket
249
- ): void {
250
- // TODO: keep the Connection object in the closure here instead of always
251
- // doing many (expensive) lookups.
252
-
253
- // set message handler for incoming messages
254
- webSocket.on("message", (data: ws.RawData) => {
255
- void (async () => {
256
- try {
257
- const msgStr = (data as Buffer).toString("utf8");
258
- logger.info(
259
- `[ConnectionManager] Message from ${connectionId}: ${msgStr}`
260
- );
261
- const message = JSON.parse(
262
- msgStr
263
- ) as ClientConnectionMessage<ClientMsgT>;
264
- await this.routeMessage(connectionId, message);
265
- } catch (error) {
266
- logger.error(`[ConnectionManager] Error processing message:`, error);
267
- this.handleError(connectionId, error);
268
- }
269
- })();
270
- });
271
-
272
- webSocket.on("close", () => {
273
- logger.debug(`[ConnectionManager] Connection ${connectionId} closed`);
274
- this.handleConnectionClose(connectionId);
275
- });
276
-
277
- webSocket.on("error", (error) => {
278
- logger.error(
279
- `[ConnectionManager] WebSocket error on ${connectionId}:`,
280
- error
281
- );
282
- this.handleConnectionClose(connectionId);
283
- });
284
- }
285
-
286
- /**
287
- * Route incoming message to appropriate handler based on message type
288
- */
289
- private async routeMessage(
290
- connectionId: string,
291
- msg: ClientConnectionMessage<ClientMsgT>
292
- ): Promise<void> {
293
- const conn = this.connections.get(connectionId);
294
- if (!conn) {
295
- throw new ChatErrorMessage(
296
- `Connection ${connectionId} not found for message routing`
297
- );
298
- }
299
-
300
- conn.lastMessageTime = Date.now();
301
-
302
- // Handle Connection-level messages
303
- {
304
- switch (msg.t) {
305
- case "data":
306
- await this.sessionMessageProcessor.processMessage(
307
- connectionId,
308
- conn.userId,
309
- msg.d
310
- );
311
- break;
312
- case "pong":
313
- // The only operation is to update the `lastMessageTime`, done above
314
- logger.debug(
315
- `[ConnectionManager.routeMessage] pong from ${connectionId}`
316
- );
317
- break;
318
- default: {
319
- const _: never = msg;
320
- break;
321
- }
322
- }
323
- }
324
- }
325
-
326
- private onHeartbeat(connectionId: string): void {
327
- // TODO: move this and other handling to the Connection class so we can
328
- // avoid all the per-event lookups.
329
-
330
- const conn = this.connections.get(connectionId);
331
- if (!conn) {
332
- // TODO: We intentionally don't try to clean up here, to force
333
- // correctness fo the real cleanup code.
334
- logger.error(
335
- `[onHeartbeat] unexpected callback for connId ${connectionId}`
336
- );
337
- return;
338
- }
339
-
340
- const now = Date.now();
341
- const timeSinceLast = now - conn.lastMessageTime;
342
- logger.debug(
343
- `[onHeartbeat] conn ${connectionId} since ${String(timeSinceLast)}`
344
- );
345
- if (timeSinceLast > 2 * HEARTBEAT_INTERVAL_MS) {
346
- this.forceCleanupConnection(connectionId);
347
- return;
348
- }
349
-
350
- const msg: ServerConnectionPing = { t: "ping" };
351
- this.sendConnectionMessage(connectionId, msg);
352
- }
353
-
354
- /**
355
- * Force cleanup of connection state - used for error recovery.
356
- * This method is safe to call multiple times and handles partial state.
357
- */
358
- private forceCleanupConnection(connectionId: string): void {
359
- const conn = this.connections.get(connectionId);
360
- if (conn) {
361
- // Cancel the heartbeat interval
362
- clearInterval(conn.heartbeat);
363
-
364
- // Remove from user connections
365
- const userConnections = this.userToConnections.get(conn.userId);
366
- if (userConnections) {
367
- userConnections.delete(connectionId);
368
- if (userConnections.size === 0) {
369
- this.userToConnections.delete(conn.userId);
370
- const _ = this.sessionMessageProcessor.handleUserDisconnect(
371
- conn.userId
372
- );
373
- }
374
- }
375
-
376
- this.connections.delete(connectionId);
377
- }
378
-
379
- logger.debug(
380
- `[ConnectionManager] Force cleaned up connection ${connectionId}`
381
- );
382
- }
383
-
384
- /**
385
- * Handle connection close - cleanup all state.
386
- * This is the normal cleanup path triggered by WebSocket events.
387
- */
388
- private handleConnectionClose(connectionId: string): void {
389
- this.forceCleanupConnection(connectionId);
390
- logger.info(`[ConnectionManager] Connection ${connectionId} closed`);
391
- }
392
-
393
- private sendConnectionMessage(
394
- connectionId: string,
395
- message: ServerConnectionMessage<ServerMsgT>
396
- ): void {
397
- const conn = this.connections.get(connectionId);
398
- if (!conn) {
399
- logger.warn(
400
- `[ConnectionManager] Cannot send to connection ${connectionId} - ` +
401
- `not found`
402
- );
403
- return;
404
- }
405
-
406
- const webSocket = conn.ws;
407
- if (webSocket.readyState !== 1) {
408
- // WebSocket.OPEN = 1
409
- logger.warn(
410
- `[ConnectionManager] Cannot send to connection ${connectionId} - ` +
411
- `not open`
412
- );
413
- return;
414
- }
415
-
416
- const msgString = JSON.stringify(message);
417
- logger.debug(
418
- `[ConnectionManager] Sending to ${connectionId}: ${msgString}`
419
- );
420
- webSocket.send(msgString);
421
- }
422
-
423
- /**
424
- * Send message to specific connection.
425
- */
426
- public sendToConnection(connectionId: string, message: ServerMsgT): void {
427
- this.sendConnectionMessage(connectionId, { t: "data", d: message });
428
- }
429
-
430
- /**
431
- * Send error message to connection
432
- */
433
- private sendConnectionError(
434
- connectionId: string,
435
- errorMessage: string
436
- ): void {
437
- const errorMsg: ServerConnectionError = {
438
- t: "error",
439
- e: errorMessage,
440
- };
441
- this.sendConnectionMessage(connectionId, errorMsg);
442
- }
443
-
444
- /**
445
- * Handle errors during message processing
446
- */
447
- private handleError(connectionId: string, error: unknown): void {
448
- let message: string;
449
- let shouldClose = false;
450
-
451
- if (typeof error === "string") {
452
- message = error;
453
- } else if (error instanceof ChatFatalError) {
454
- message = error.message;
455
- shouldClose = true;
456
- } else if (error instanceof ChatErrorMessage) {
457
- message = error.message;
458
- } else if (error instanceof Error) {
459
- message = "Internal server error: " + error.message;
460
- } else {
461
- message = "Unknown error occurred";
462
- }
463
-
464
- logger.warn(
465
- `[ConnectionManager] Error on connection ${connectionId}: ${message}`
466
- );
467
- this.sendConnectionError(connectionId, message);
468
-
469
- if (shouldClose) {
470
- const conn = this.connections.get(connectionId);
471
- logger.info(
472
- `[ConnectionManager] Closing connection ${connectionId} ` +
473
- `due to error: ${message}`
474
- );
475
- if (conn) {
476
- conn.ws.close(4000, message);
477
- }
478
- }
479
- }
480
-
481
- /**
482
- * Implementation of IUserConnectionManager interface.
483
- * Send message to all active connections of specific users.
484
- */
485
- public sendToUsers(userIds: Set<string>, message: ServerMsgT): void {
486
- for (const userId of userIds.values()) {
487
- const connectionIds = this.getUserConnections(userId);
488
- for (const connectionId of connectionIds) {
489
- logger.debug(
490
- `[ConnectionManager] sending to user ${userId} ` +
491
- `connection ${connectionId}`
492
- );
493
- this.sendToConnection(connectionId, message);
494
- }
495
- }
496
- }
497
-
498
- /**
499
- * Get all active connection IDs for a user.
500
- * Helper method for sendToUsers implementation.
501
- */
502
- private getUserConnections(userId: string): string[] {
503
- const connections = this.userToConnections.get(userId);
504
- return connections ? Array.from(connections) : [];
505
- }
506
- }