@jungjaehoon/mama-core 1.1.3 → 1.1.4

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.
@@ -21,7 +21,9 @@ import type { Server as HTTPServer } from 'http';
21
21
  * Message router interface
22
22
  */
23
23
  interface MessageRouter {
24
- process(message: NormalizedMessage): Promise<RouterResult>;
24
+ process(message: NormalizedMessage, options?: {
25
+ onQueued?: () => void;
26
+ }): Promise<RouterResult>;
25
27
  }
26
28
  /**
27
29
  * Session store interface
@@ -81,6 +81,22 @@ function extractUserId(req) {
81
81
  return `user_${Date.now()}`;
82
82
  }
83
83
  }
84
+ /**
85
+ * Safe send helper - guards against closed socket with try-catch for race conditions
86
+ */
87
+ function safeSend(ws, data) {
88
+ if (ws.readyState === ws_1.WebSocket.OPEN) {
89
+ try {
90
+ ws.send(data);
91
+ return true;
92
+ }
93
+ catch {
94
+ // Socket may have closed between readyState check and send
95
+ return false;
96
+ }
97
+ }
98
+ return false;
99
+ }
84
100
  /**
85
101
  * Create WebSocket handler with MessageRouter integration
86
102
  */
@@ -269,19 +285,37 @@ async function handleClientMessage(clientId, message, clientInfo, messageRouter,
269
285
  timestamp: Date.now(),
270
286
  },
271
287
  };
272
- result = await messageRouter.process(normalizedMessage);
288
+ result = await messageRouter.process(normalizedMessage, {
289
+ onQueued: () => {
290
+ // Guard: check if socket is still open before sending
291
+ if (clientInfo.ws.readyState !== ws_1.WebSocket.OPEN) {
292
+ return;
293
+ }
294
+ // Notify client that message is queued (session busy)
295
+ try {
296
+ clientInfo.ws.send(JSON.stringify({
297
+ type: 'queued',
298
+ message: '⏳ 이전 요청 처리 중... 대기열에 추가되었습니다.',
299
+ }));
300
+ }
301
+ catch (err) {
302
+ logger.warn('Queued notification failed:', err instanceof Error ? err.message : String(err));
303
+ }
304
+ logger.info(`Message queued for ${clientId} (session busy)`);
305
+ },
306
+ });
273
307
  }
274
308
  finally {
275
309
  clearInterval(keepAliveInterval);
276
310
  }
277
311
  // Send response as stream (for Chat tab compatibility)
278
- clientInfo.ws.send(JSON.stringify({
312
+ safeSend(clientInfo.ws, JSON.stringify({
279
313
  type: 'stream',
280
314
  content: result.response,
281
315
  sessionId: result.sessionId,
282
316
  }));
283
317
  // Send stream end
284
- clientInfo.ws.send(JSON.stringify({
318
+ safeSend(clientInfo.ws, JSON.stringify({
285
319
  type: 'stream_end',
286
320
  sessionId: result.sessionId,
287
321
  duration: result.duration,
@@ -291,7 +325,7 @@ async function handleClientMessage(clientId, message, clientInfo, messageRouter,
291
325
  catch (error) {
292
326
  const errMsg = error instanceof Error ? error.message : String(error);
293
327
  logger.error(`Message processing error for ${clientId}:`, errMsg);
294
- clientInfo.ws.send(JSON.stringify({
328
+ safeSend(clientInfo.ws, JSON.stringify({
295
329
  type: 'error',
296
330
  error: 'Failed to process message',
297
331
  // Don't expose internal error details to client for security
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungjaehoon/mama-core",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "MAMA Core - Shared modules for Memory-Augmented MCP Assistant",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",