From f2f6a620243fb934cc1ceec2383e592f36cbd59c Mon Sep 17 00:00:00 2001 From: Lily Shen Date: Mon, 4 May 2026 13:06:05 -0700 Subject: [PATCH] fix(messages): emit Socket.io newMessage on user-message POST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, only the agent-runtime path (`agentMessageService.postMessage`) emitted a `newMessage` Socket.io event when a message landed. The human- user path (`messageController.createMessage`) saved to PG/Mongo and returned the row, but never broadcast — so when a human posted in a pod, every other connected user only saw the message on manual refresh. Symptom: chat looks broken for everyone except the message author. Easy to miss in dev because the author's own client renders the message optimistically from the response, so only second-user testing surfaces the gap. Fix: mirror the broadcast shape from `agentMessageService` (same `pod_id` + `podId` both populated for V2's cross-pod-leak filter, same fallback ordering for `_id`/`id`, same field names). Wrapped in try/catch so a broken socket layer can't fail the message post. Surfaced during YC demo recording — Mike's user-token messages weren't showing up live in Sam's tab while Cody's agent-runtime messages were. Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/controllers/messageController.ts | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/backend/controllers/messageController.ts b/backend/controllers/messageController.ts index 8023417e..6de6f026 100644 --- a/backend/controllers/messageController.ts +++ b/backend/controllers/messageController.ts @@ -240,6 +240,39 @@ exports.createMessage = async (req: AuthRequest, res: Response): Promise = await AgentMentionService.enqueueMentions({ podId, message, userId, username }); } + // Live-broadcast the new message to every connected client in this pod's + // Socket.io room. Without this emit, V2 chat surfaces only see the new + // message on a manual refresh — the agent-runtime path (`AgentMessageService. + // postMessage`) emits `newMessage` already, but the human-user path + // never did, which made human messages "disappear" until refresh from + // the perspective of every other connected user. + // + // Same payload shape that `agentMessageService` uses (`pod_id` + `podId` + // both populated for V2's cross-pod-leak guard in useV2PodDetail). + try { + // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require + const socketConfig = require('../config/socket'); + const io = socketConfig.getIO(); + if (io) { + const m = message as NormalizedMessage & { _id?: string; id?: string; userId?: any; profile_picture?: string; createdAt?: string }; + const formattedMessage = { + _id: m._id || m.id, + id: m._id || m.id, + pod_id: String(podId), + podId: String(podId), + content: m.content, + messageType: (m as any).messageType || 'text', + userId: m.userId, + username: m.username, + profile_picture: m.profile_picture, + createdAt: m.createdAt, + }; + io.to(`pod_${podId}`).emit('newMessage', formattedMessage); + } + } catch (socketErr) { + console.warn('[messageController] socket emit failed:', (socketErr as Error).message); + } + res.json(message); } catch (err) { const e = err as { message?: string; kind?: string };