Skip to content

fix: broadcast message created events - #11

Merged
Rieranthony merged 5 commits into
mainfrom
codex/implement-message-sending-functionality
Sep 17, 2025
Merged

Rieranthony merged 5 commits into
mainfrom
codex/implement-message-sending-functionality

Conversation

@Rieranthony

@Rieranthony Rieranthony commented Sep 17, 2025 •

Copy link
Copy Markdown
Contributor

Summary

  • add a shared createMessage helper that emits realtime message events and reuse it from the REST and TRPC conversation APIs
  • extend the realtime schema/handlers for MESSAGE_CREATED notifications, log them on the websocket router, and broadcast them to connected clients
  • wire the dashboard conversation UI for optimistic sends with centralized cache utilities and realtime debugging logs

Testing

  • bun test apps/api/src/ws/router.test.ts
  • bunx turbo run lint --filter=@cossistant/web --filter=@cossistant/api (fails: biome command missing in environment)

https://chatgpt.com/codex/tasks/task_e_68cac2ed3048832b8efce8621e9880cd

Summary by CodeRabbit

  • New Features

    • New sendMessage route for posting messages (text/image/file, public/private) and realtime MESSAGE_CREATED events.
    • Client-side cache helpers for infinite conversation message lists (upsert/remove/query key).
    • Message payload now includes parentMessageId and modelUsed fields.
  • Refactor

    • Standardized per-message creation flow with normalized defaults and realtime emission.
  • Tests

    • Added tests verifying MESSAGE_CREATED events are forwarded to all channels.

@vercel

vercel Bot commented Sep 17, 2025 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
web Error Error Sep 17, 2025 4:53pm

@coderabbitai

coderabbitai Bot commented Sep 17, 2025 •

Copy link
Copy Markdown
Contributor

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces bulk message inserts with a typed per-message createMessage flow, adds a TRPC sendMessage mutation, emits realtime MESSAGE_CREATED events (broadcast via WS), updates REST handlers to call createMessage, adds client-side conversation message cache helpers, and expands message schema with parentMessageId and modelUsed.

Changes

Cohort / File(s) Summary of changes
REST routers: per-message creation
apps/api/src/rest/routers/conversation.ts, apps/api/src/rest/routers/messages.ts
Replaced bulk sendMessages usage with per-message createMessage calls; changed message payload shape and defaulting (type/visibility/createdAt/user/agent/visitor handling); updated imports and return handling (array → single Message).
TRPC conversation route
apps/api/src/trpc/routers/conversation.ts
Added sendMessage mutation that validates input, verifies website/conversation access, calls createMessage, and returns the created message.
Message utilities and normalization
apps/api/src/utils/message.ts
New CreateMessageOptions and exported createMessage(...) which normalizes body, sets defaults, persists via underlying DB insert, parses message, emits MESSAGE_CREATED realtime event, and returns the parsed Message; prepareMessageForInsert now returns { bodyMd }.
Realtime WS handling and tests
apps/api/src/ws/router.ts, apps/api/src/ws/router.test.ts
Imported emitToAll, added MESSAGE_CREATED handler that calls emitToAll(conversationId, websiteId, "MESSAGE_CREATED", data); tests mock and assert emitToAll invocation and reset mocks between tests.
Web cache helpers
apps/web/src/data/conversation-message-cache.ts
New module providing types and helpers for InfiniteData pages: sort, initialize, upsert, remove messages, and cache mutators (createConversationMessagesInfiniteQueryKey, upsertConversationMessageInCache, removeConversationMessageFromCache).
Web data hook refactor
apps/web/src/data/use-conversation-messages.tsx
Uses createConversationMessagesInfiniteQueryKey for the infinite query key, introduces optional enabled?: boolean in options, preserves pagination and fetch logic.
Schema / types
packages/types/src/schemas.ts, packages/types/src/realtime-events.ts
Added parentMessageId and modelUsed (nullable strings) to message schema/type; added MESSAGE_CREATED realtime event type with message + context payload; imported message enums for event typing.
Minor UI / formatting
apps/web/src/components/conversation/index.tsx, apps/web/src/app/(lander-docs)/components/docs/mdx-components.tsx
Formatting/indentation/reflow changes with no behavioral impact.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as Web UI
  participant REST as REST API
  participant TRPC as TRPC API
  participant Util as createMessage (utils/message)
  participant DB as DB (sendMessages / insert)
  participant Pub as PubSub (publish)
  participant WS as WS Router
  participant Clients as Subscribed Clients

  UI->>TRPC: sendMessage({conversationId, websiteSlug, bodyMd, type, visibility})
  TRPC->>Util: createMessage({db, orgId, websiteId, conversationId, message})
  Util->>DB: insert message (per-message)
  DB-->>Util: created Message
  Util->>Pub: publish(MESSAGE_CREATED, { message, conversationId, websiteId, organizationId })
  Util-->>TRPC: Message
  TRPC-->>UI: { message }

  Pub->>WS: routeEvent(MESSAGE_CREATED)
  WS->>Clients: emitToAll(conversationId, websiteId, "MESSAGE_CREATED", data)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

A rabbit typed lines, one message at a time,
Each hop became payload, neat, small, and sublime.
A ping to the server, a realtime beam,
Messages born, sent, and stitched into the stream. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix: broadcast message created events" is concise and accurately reflects the primary change in the PR—adding/broadcasting message-created realtime events (createMessage helper, realtime schema/handler updates, and websocket broadcasts) as described in the PR objectives, so a teammate scanning history will understand the main intent.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-message-sending-functionality

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/api/src/rest/routers/messages.ts (1)

195-204: Return 400 for missing Visitor ID (currently returns 200)

OpenAPI declares 400 for invalid requests, but this path returns a 200 with an error payload.

-    if (!visitorId) {
-      return c.json(
-        validateResponse(
-          { error: "Visitor ID is required" },
-          z.object({ error: z.string() }),
-        ),
-      );
-    }
+    if (!visitorId) {
+      return c.json(
+        { error: "Visitor ID is required" },
+        400,
+      );
+    }
apps/api/src/rest/routers/conversation.ts (1)

404-414: Remove sensitive debug logging.

This logs Authorization/Public keys; violates least‑logging and “don’t use console.”

-        // Debug: Check what's in the context
-        console.log("[DEBUG] GET /conversations/{id} - Context check:", {
-          hasDb: !!c.get("db"),
-          hasWebsite: !!c.get("website"),
-          hasOrganization: !!c.get("organization"),
-          hasApiKey: !!c.get("apiKey"),
-          headers: {
-            authorization: c.req.header("Authorization"),
-            publicKey: c.req.header("X-Public-Key"),
-            visitorId: c.req.header("X-Visitor-Id"),
-          },
-        });
+        // Debug logging removed: avoid emitting secrets in logs
🧹 Nitpick comments (11)
apps/web/src/data/use-conversation-messages.tsx (2)

7-10: Naming consistency: this is “messages”, not “headers”

Rename the options interface to match the hook’s domain.

-interface UseConversationHeadersOptions {
+interface UseConversationMessagesOptions {
   limit?: number;
   enabled?: boolean;
 }
 …
-export function useConversationMessages({
+export function useConversationMessages({
   websiteSlug,
   conversationId,
   options,
 }: {
   websiteSlug: string;
   conversationId: string;
-  options?: UseConversationHeadersOptions;
+  options?: UseConversationMessagesOptions;
 }) {

Also applies to: 12-20


29-39: Optional: avoid double-caching via fetchQuery

Using queryClient.fetchQuery with a different key than useInfiniteQuery.queryKey writes twice to cache. Prefer calling the TRPC fetcher directly (or queryFn inline) to keep a single cache path.

apps/web/src/data/conversation-message-cache.ts (1)

79-110: Prune empty pages after deletions

After removals, pages with zero items are retained. Trim them to reduce noise and re-renders.

   const pages = existing.pages.map((page) => {
     const filtered = page.items.filter((item) => item.id !== messageId);
     if (filtered.length !== page.items.length) {
       removed = true;
       return {
         ...page,
         items: filtered,
       };
     }
     return page;
   });

   if (!removed) {
     return existing;
   }

-  return {
-    pages,
-    pageParams: [...existing.pageParams],
-  };
+  const prunedPages = pages.filter((p) => p.items.length > 0);
+  const prunedParams =
+    prunedPages.length === pages.length
+      ? [...existing.pageParams]
+      : existing.pageParams.slice(0, prunedPages.length);
+  return {
+    pages: prunedPages,
+    pageParams: prunedParams,
+  };
apps/api/src/ws/router.ts (1)

32-44: Replace console. with centralized logger*

Guidelines prohibit console in runtime code. Use a structured logger (info/warn/error) and ensure PII is not logged.

- console.log(`[USER_CONNECTED] User ${data.userId} connected`, { … });
+ logger.info(`[USER_CONNECTED] User ${data.userId} connected`, { … });

- console.error(`[EventRouter] Error handling ${event.type}:`, error);
+ logger.error({ err: error, eventType: event.type }, "[EventRouter] handler error");

Also applies to: 46-58, 60-72, 74-89, 91-105, 134-145

packages/types/src/realtime-events.ts (2)

34-58: Use zod’s datetime validator for timestamp strings

Declare timestamp strings as ISO datetimes for stronger validation and clearer intent.

-      createdAt: z.string(),
-      updatedAt: z.string(),
-      deletedAt: z.string().nullable(),
+      createdAt: z.string().datetime(),
+      updatedAt: z.string().datetime(),
+      deletedAt: z.string().datetime().nullable(),

Also consider documenting timezone expectations (UTC vs local) to avoid ambiguity.


61-67: Minor: document event field units and constraints

Add brief JSDoc that timestamp and presence lastSeen are epoch millis, and that message.type/visibility mirror the enums from @cossistant/types.

Also applies to: 69-82

apps/api/src/utils/message.ts (2)

79-99: Defensive check: ensure DB returned a row.

sendMessages should return one row; handle empty/undefined to avoid runtime errors.

         const [createdMessage] = await sendMessages(db, {
           ...
         });
 
-        const parsedMessage = messageSchema.parse(createdMessage);
+        if (!createdMessage) {
+                throw new Error("Failed to create message: insert returned no rows");
+        }
+        const parsedMessage = messageSchema.parse(createdMessage);

16-20: Optional: expand normalization + docs.

Consider trimming and normalizing newlines; add JSDoc since this is part of the public flow.

-export function prepareMessageForInsert(bodyMd: string) {
-        const normalized = bodyMd.normalize("NFC");
-        return { bodyMd: normalized };
+/**
+ * Normalize message markdown before insert.
+ * - NFC normalization
+ * - Optional whitespace/newline normalization (non-destructive)
+ */
+export function prepareMessageForInsert(bodyMd: string) {
+        const normalized = bodyMd.normalize("NFC");
+        return { bodyMd: normalized };
 }
apps/web/src/components/conversation/index.tsx (3)

71-75: More collision‑resistant optimistic IDs.

Date.now() can collide under fast sends. Prefer crypto.randomUUID() with a fallback.

-        const optimisticMessageId = `optimistic-${Date.now()}`;
+        const optimisticMessageId =
+          `optimistic-${(globalThis.crypto?.randomUUID?.() ??
+            `${Date.now()}-${Math.random().toString(36).slice(2)}`)}`;

157-167: Handle MESSAGE_CREATED by updating the cache, not logging.

Apply event to the infinite list so other clients’ messages appear in real time.

-  const handleRealtimeEvent = useCallback(
-    (event: RealtimeEvent) => {
-      if (
-        event.type === "MESSAGE_CREATED" &&
-        event.data.conversationId === conversationId
-      ) {
-        console.log("[Conversation] MESSAGE_CREATED event received", event.data);
-      }
-    },
-    [conversationId],
-  );
+  const handleRealtimeEvent = useCallback(
+    (event: RealtimeEvent) => {
+      if (event.type !== "MESSAGE_CREATED") return;
+      if (event.data.conversationId !== conversationId) return;
+      const m = event.data.message;
+      const normalized: ConversationMessage = {
+        ...m,
+        createdAt: new Date(m.createdAt),
+        updatedAt: new Date(m.updatedAt),
+        deletedAt: m.deletedAt ? new Date(m.deletedAt) : null,
+      };
+      upsertConversationMessageInCache(
+        queryClient,
+        messagesInfiniteQueryKey,
+        normalized,
+      );
+    },
+    [conversationId, queryClient, messagesInfiniteQueryKey],
+  );

163-164: Remove console usage in UI.

Avoid console.* per guidelines; the cache update above supersedes logging.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7ebc1c and 78ed52e.

📒 Files selected for processing (10)
  • apps/api/src/rest/routers/conversation.ts (2 hunks)
  • apps/api/src/rest/routers/messages.ts (2 hunks)
  • apps/api/src/trpc/routers/conversation.ts (3 hunks)
  • apps/api/src/utils/message.ts (1 hunks)
  • apps/api/src/ws/router.test.ts (3 hunks)
  • apps/api/src/ws/router.ts (2 hunks)
  • apps/web/src/components/conversation/index.tsx (3 hunks)
  • apps/web/src/data/conversation-message-cache.ts (1 hunks)
  • apps/web/src/data/use-conversation-messages.tsx (2 hunks)
  • packages/types/src/realtime-events.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)

**/*.{ts,tsx}: Manage shared component state via React Context
Implement full TypeScript support for all public APIs
Provide proper type definitions for all props and refs
Use appropriate generic constraints where necessary
Export type definitions for the public API
Avoid TypeScript enums; prefer const objects with 'as const' or Object.freeze
Provide clear, descriptive runtime errors for missing context/props and misuse
Expose dedicated hooks for context access that throw helpful errors when context is missing
Add explicit error messages for: outside-context usage, missing required props, invalid prop combinations, and validation failures
Document each component with JSDoc: purpose, props (required/optional), context requirements, example usage, pitfalls
State management: use React Context for global state, perform correct state updates, and handle side effects appropriately
Validation: implement runtime checks and validate prop combinations with clear errors
Create context with createContext<ContextType|null>(null) and a useContext hook that throws a clear error when missing

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't have unused function parameters
Don't have unused private class members
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Ensure switch-case statements are exhaustive (especially with unions)
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types (where discouraged by policy)
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibili...

Files:

  • apps/api/src/ws/router.test.ts
  • apps/web/src/components/conversation/index.tsx
  • apps/api/src/trpc/routers/conversation.ts
  • apps/api/src/ws/router.ts
  • apps/web/src/data/conversation-message-cache.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/utils/message.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/types/src/realtime-events.ts
**/*.{test.ts,test.tsx,spec.ts,spec.tsx,test.js,test.jsx,spec.js,spec.jsx}

📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)

Include tests: unit tests for components, integration tests for composed components, accessibility tests, and error boundary tests

Files:

  • apps/api/src/ws/router.test.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use the accessKey attribute on any HTML element
Use correct ISO language/country codes for the lang attribute
Don't hardcode sensitive data like API keys and tokens
Use === and !==

Files:

  • apps/api/src/ws/router.test.ts
  • apps/web/src/components/conversation/index.tsx
  • apps/api/src/trpc/routers/conversation.ts
  • apps/api/src/ws/router.ts
  • apps/web/src/data/conversation-message-cache.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/utils/message.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/types/src/realtime-events.ts
**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Prefer for...of statements instead of Array.forEach
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Avoid ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use Date.now() to get milliseconds since the Unix Epoch
Prefer .flatMap() over map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when numeric literals suffice (binary/octal/hex)
Use concise optional chaining instead of chained logical expressions
Prefer regular expression literals over the RegExp constructor when possible
Don't use non–base-10 or underscore-separated number literal object member names
Remove redundant terms from logical expressions
Prefer while loops over for loops when no initializer/update is needed
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min/Math.max to clamp values when result is constant
Don't use empty character classes in regular expression literals
Don't use empty destructuring patterns
Don't call global object properties as functions
Don't declare functions and vars that are acce...

Files:

  • apps/api/src/ws/router.test.ts
  • apps/api/src/trpc/routers/conversation.ts
  • apps/api/src/ws/router.ts
  • apps/web/src/data/conversation-message-cache.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/api/src/utils/message.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/types/src/realtime-events.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Ensure the assertion function (e.g., expect) is placed inside an it() function call
Don't use disabled tests

Files:

  • apps/api/src/ws/router.test.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts,tsx}: Don't create classes that only have static members
Don't return a value from a constructor
Don't use super() incorrectly inside classes; ensure super() is called in derived constructors
Ensure super() is called exactly once on every code path in a class constructor before this is accessed if the class has a superclass
Place getters and setters for the same property next to each other in class and object definitions
Ensure JSDoc comment lines start with a single asterisk, except for the first one
Don't use duplicate class members

Files:

  • apps/api/src/ws/router.test.ts
  • apps/web/src/components/conversation/index.tsx
  • apps/api/src/trpc/routers/conversation.ts
  • apps/api/src/ws/router.ts
  • apps/web/src/data/conversation-message-cache.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/utils/message.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/types/src/realtime-events.ts
**/*.{js,tsx,ts,jsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

Use arrow functions instead of function expressions

Files:

  • apps/api/src/ws/router.test.ts
  • apps/web/src/components/conversation/index.tsx
  • apps/api/src/trpc/routers/conversation.ts
  • apps/api/src/ws/router.ts
  • apps/web/src/data/conversation-message-cache.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/utils/message.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/types/src/realtime-events.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts,jsx,tsx}: Prevent duplicate polyfills from Polyfill.io
Don't use irregular whitespace characters

Files:

  • apps/api/src/ws/router.test.ts
  • apps/web/src/components/conversation/index.tsx
  • apps/api/src/trpc/routers/conversation.ts
  • apps/api/src/ws/router.ts
  • apps/web/src/data/conversation-message-cache.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/utils/message.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/types/src/realtime-events.ts
**/*.{js,ts,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

Don't redeclare variables, functions, classes, and types in the same scope

Files:

  • apps/api/src/ws/router.test.ts
  • apps/web/src/components/conversation/index.tsx
  • apps/api/src/trpc/routers/conversation.ts
  • apps/api/src/ws/router.ts
  • apps/web/src/data/conversation-message-cache.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/utils/message.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/types/src/realtime-events.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)

**/*.tsx: Use the Compound Components pattern with namespaced exports (e.g., FormWizard.Root, FormWizard.Step) so pieces compose under a single parent
Ensure accessibility: keyboard navigation support
Ensure accessibility: include appropriate ARIA attributes
Ensure accessibility: use semantic markup for screen readers
Maintain proper focus management in interactive components
Structure components with forwardRef and typed props/refs as shown (ComponentRef, HTMLAttributes, ReactNode)
Root component acts as main context provider, manages global state, and handles initialization
Child components must be used within Root, read context via hooks, support asChild, and follow consistent props
Props ordering: list required props first; optional props have clear defaults; support common HTML attributes; expose className
Support composition with an asChild prop using Radix UI Slot to allow element replacement
Styling: support className, use data-* attributes for state, and allow style customization
Event handling: support standard DOM events, add custom events when needed, and manage propagation correctly
Base component pattern: forwardRef, choose Slot or intrinsic element via asChild, pass ref/className and spread props

Files:

  • apps/web/src/components/conversation/index.tsx
  • apps/web/src/data/use-conversation-messages.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't set aria-hidden="true" on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like or
Only use the scope prop on elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Ensure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assign tabIndex to non-interactive HTML elements
Don't use positive integers for tabIndex property
Don't include the words "image", "picture", or "photo" in img alt prop
Don't use an explicit role that's the same as the element's implicit role
Static elements with click handlers must have a valid role attribute
Always include a <title> element within SVG elements
Provide meaningful alt text for elements that require alt text
Ensure anchors have content accessible to screen readers
Assign tabIndex to non-interactive elements only when using aria-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Ensure ARIA properties are valid for the element's supported roles
Always include a type attribute for button elements
Elements with interactive roles and handlers must be focusable
Heading elements must have content accessible to screen readers (not hidden with aria-hidden)
Always include a lang attribute on the html element
Always include a title attribute for iframe elements
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress
Accompany onMouseOver/onMouseOut with onFocus/onBlur
Include caption tracks for audio and video elements
Prefer semantic elements instead of role attributes in JSX
Ensure all anchors are valid and navigable
Ensure all aria-* properties are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid ARIA state and property values
Use valid values for the autocomplete attribute on input elements
Don't use unnecessary fragmen...

Files:

  • apps/web/src/components/conversation/index.tsx
  • apps/web/src/data/use-conversation-messages.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx,html}: Ensure the preconnect attribute is used when using Google Fonts
Don't use target="_blank" without rel="noopener"
Use a recommended display strategy with Google Fonts

Files:

  • apps/web/src/components/conversation/index.tsx
  • apps/web/src/data/use-conversation-messages.tsx
packages/types/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

packages/types/src/**/*.ts: Keep shared TypeScript types and Zod schemas in packages/types/src/ and update them alongside schema changes
Ensure Zod schemas mirror any API/database shape changes for end-to-end type safety

Files:

  • packages/types/src/realtime-events.ts
🧬 Code graph analysis (7)
apps/api/src/ws/router.test.ts (3)
apps/api/src/lib/pubsub.ts (1)
  • emitToAll (591-598)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (63-67)
apps/api/src/ws/router.ts (1)
  • routeEvent (128-146)
apps/web/src/components/conversation/index.tsx (6)
apps/web/src/lib/trpc/client.tsx (1)
  • useTRPC (18-18)
apps/web/src/data/conversation-message-cache.ts (5)
  • createConversationMessagesInfiniteQueryKey (112-116)
  • ConversationMessagesPage (4-5)
  • ConversationMessage (6-7)
  • upsertConversationMessageInCache (118-127)
  • removeConversationMessageFromCache (129-138)
packages/types/src/enums.ts (4)
  • MessageType (1-5)
  • MessageType (7-7)
  • MessageVisibility (33-36)
  • MessageVisibility (74-75)
packages/react/src/hooks/use-multimodal-input.ts (1)
  • useMultimodalInput (31-187)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (63-67)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/websocket.tsx (1)
  • useDashboardRealtime (251-274)
apps/api/src/trpc/routers/conversation.ts (5)
apps/api/src/trpc/init.ts (1)
  • protectedProcedure (52-54)
packages/types/src/enums.ts (4)
  • MessageType (1-5)
  • MessageType (7-7)
  • MessageVisibility (33-36)
  • MessageVisibility (74-75)
packages/types/src/schemas.ts (1)
  • messageSchema (9-21)
apps/api/src/db/queries/website.ts (1)
  • getWebsiteBySlugWithAccess (229-243)
apps/api/src/utils/message.ts (1)
  • createMessage (68-112)
apps/api/src/ws/router.ts (1)
apps/api/src/lib/pubsub.ts (2)
  • emitToDashboard (583-589)
  • emitToAll (591-598)
apps/api/src/rest/routers/messages.ts (3)
apps/api/src/utils/message.ts (1)
  • createMessage (68-112)
apps/api/src/utils/validate.ts (1)
  • validateResponse (6-20)
packages/types/src/api/message.ts (1)
  • sendMessageResponseSchema (40-42)
apps/web/src/data/use-conversation-messages.tsx (1)
apps/web/src/data/conversation-message-cache.ts (1)
  • createConversationMessagesInfiniteQueryKey (112-116)
apps/api/src/utils/message.ts (6)
apps/api/src/db/index.ts (2)
  • Database (34-34)
  • db (36-41)
packages/types/src/enums.ts (4)
  • MessageType (1-5)
  • MessageType (7-7)
  • MessageVisibility (33-36)
  • MessageVisibility (74-75)
packages/types/src/schemas.ts (2)
  • Message (23-23)
  • messageSchema (9-21)
packages/types/src/realtime-events.ts (1)
  • RealtimeEventData (69-71)
apps/api/src/db/queries/message.ts (1)
  • sendMessages (7-46)
apps/api/src/lib/pubsub.ts (1)
  • emitToAll (591-598)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (7)
apps/api/src/ws/router.ts (1)

107-122: MESSAGE_CREATED broadcast looks correct

Forwarding to all relevant channels with emitToAll(conversationId, websiteId, "MESSAGE_CREATED", data) aligns with the new event contract.

apps/api/src/ws/router.test.ts (2)

80-121: Good coverage: verifies MESSAGE_CREATED is forwarded via emitToAll

The mock and assertion validate the pubsub forwarding path end-to-end.


5-10: Nice: emitToAll is mocked/reset alongside emitToDashboard

Keeps tests isolated and avoids cross-test leakage.

Also applies to: 18-21

apps/api/src/trpc/routers/conversation.ts (1)

211-269: sendMessage mutation is well‑scoped and consistent with server invariants

Validates access, binds conversation to website, creates a single message via createMessage, and returns messageSchema. Looks good.

apps/api/src/rest/routers/messages.ts (1)

204-223: No change required — request schema already requires Date for createdAt

sendMessageRequestSchema declares createdAt: z.date().optional() (packages/types/src/api/message.ts), so validated body.message.createdAt should already be a Date; only add a conversion if you plan to accept string timestamps (use z.coerce.date() or convert before calling createMessage).

apps/web/src/data/conversation-message-cache.ts (1)

38-77: Pagination invariant: don't assume the last page is the newest

Server-side message queries use both DESC and ASC on createdAt (apps/api/src/db/queries/message.ts — .orderBy(desc(message.createdAt)) and .orderBy(asc(message.createdAt)); getConversationMessages is invoked from apps/api/src/trpc/routers/conversation.ts:132). Current upsert appends a missing message to the last page — instead insert by createdAt into the page that matches the message timestamp (or re-fetch/merge/sort pages), or standardize the server ordering/cursor semantics.

apps/api/src/utils/message.ts (1)

38-66: No change required — realtime schema expects ISO strings.
MESSAGE_CREATED's message.createdAt/updatedAt/deletedAt are defined as z.string() in packages/types/src/realtime-events.ts, so using toISOString() is correct.

Comment thread apps/api/src/rest/routers/conversation.ts Outdated
Comment thread apps/api/src/utils/message.ts Outdated
Comment on lines +100 to +109
await emitToAll(
conversationId,
websiteId,
"MESSAGE_CREATED",
serializeMessageForRealtime(parsedMessage, {
conversationId,
websiteId,
organizationId,
}),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t fail writes if pubsub emit errors; catch and downgrade to telemetry.

Event emission shouldn’t break message creation. Wrap in try/catch (or .catch) and avoid console logging.

Apply:

-        await emitToAll(
-                conversationId,
-                websiteId,
-                "MESSAGE_CREATED",
-                serializeMessageForRealtime(parsedMessage, {
-                        conversationId,
-                        websiteId,
-                        organizationId,
-                }),
-        );
+        try {
+                await emitToAll(
+                        conversationId,
+                        websiteId,
+                        "MESSAGE_CREATED",
+                        serializeMessageForRealtime(parsedMessage, {
+                                conversationId,
+                                websiteId,
+                                organizationId,
+                        }),
+                );
+        } catch {
+                // Non-blocking: record to telemetry/logging; do not throw
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await emitToAll(
conversationId,
websiteId,
"MESSAGE_CREATED",
serializeMessageForRealtime(parsedMessage, {
conversationId,
websiteId,
organizationId,
}),
);
try {
await emitToAll(
conversationId,
websiteId,
"MESSAGE_CREATED",
serializeMessageForRealtime(parsedMessage, {
conversationId,
websiteId,
organizationId,
}),
);
} catch {
// Non-blocking: record to telemetry/logging; do not throw
}
🤖 Prompt for AI Agents
In apps/api/src/utils/message.ts around lines 100 to 109, the call to emitToAll
can throw and currently can break message creation; wrap the emit call in a
try/catch (or append .catch) so any errors are swallowed and do not propagate,
and in the catch send the error to the project's telemetry/monitoring API (e.g.,
telemetry.captureException or equivalent) with contextual metadata
(conversationId, websiteId, organizationId) instead of using console.log, then
continue normally without rethrowing.

Comment on lines +12 to +15
return [...messages].sort(
(a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Realtime dates are strings; cache helpers expect Date objects

sortMessagesByCreatedAt calls getTime(), so any realtime payload with ISO strings will crash unless coerced. Export a small helper to normalize dates before calling upsertConversationMessageInCache.

Apply this addition near the bottom of the file:

+// Normalize ISO date strings from realtime into Date objects for cache helpers.
+export function coerceMessageDates<T extends { createdAt: unknown; updatedAt: unknown; deletedAt: unknown }>(
+  m: T,
+): T & { createdAt: Date; updatedAt: Date; deletedAt: Date | null } {
+  return {
+    ...m,
+    createdAt: m.createdAt instanceof Date ? m.createdAt : new Date(m.createdAt as string),
+    updatedAt: m.updatedAt instanceof Date ? m.updatedAt : new Date(m.updatedAt as string),
+    deletedAt:
+      m.deletedAt == null
+        ? null
+        : m.deletedAt instanceof Date
+          ? (m.deletedAt as Date)
+          : new Date(m.deletedAt as string),
+  };
+}

Use this in the UI’s realtime handler before calling upsertConversationMessageInCache(...).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return [...messages].sort(
(a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
);
}
return [...messages].sort(
(a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
);
}
// Normalize ISO date strings / numeric timestamps from realtime into Date objects for cache helpers.
export function coerceMessageDates<T extends { createdAt: unknown; updatedAt: unknown; deletedAt?: unknown }>(
m: T,
): T & { createdAt: Date; updatedAt: Date; deletedAt: Date | null } {
const toDate = (v: unknown): Date => {
if (v instanceof Date) return v;
if (typeof v === 'number') return new Date(v);
if (typeof v === 'string') return new Date(v);
throw new TypeError('Invalid date value: expected Date, string, or number.');
};
return {
...m,
createdAt: toDate(m.createdAt),
updatedAt: toDate(m.updatedAt),
deletedAt: m.deletedAt === undefined || m.deletedAt === null ? null : toDate(m.deletedAt),
};
}
🤖 Prompt for AI Agents
In apps/web/src/data/conversation-message-cache.ts around lines 12–15,
sortMessagesByCreatedAt assumes message.createdAt is a Date and calls getTime(),
which will throw for realtime payloads where createdAt is an ISO string; add and
export a small helper (e.g., normalizeMessageDates or
ensureMessageCreatedAtIsDate) near the bottom of this file that accepts a
message or array of messages and coerces any string createdAt into new
Date(createdAt) while leaving Date objects untouched, then update callers (the
UI realtime handler) to run messages through this helper before calling
upsertConversationMessageInCache(...).

Comment thread apps/web/src/data/use-conversation-messages.tsx Outdated
Rieranthony and others added 2 commits September 17, 2025 17:02
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/src/data/use-conversation-messages.tsx (2)

15-15: Wrong staleTime value (off by 1,000×) — prevents timely refreshes

Comment says 5 minutes, but 300_000_000 ms ≈ 83 hours.

Apply:

-const STALE_TIME = 300_000_000;
+const STALE_TIME = 300_000;

38-45: Cursor type mismatch: pageParam should be string (message id), not Date

API expects cursor: string (message id). Initial and subsequent pageParam values are strings. Typing it as Date risks incorrect values and confusion.

-        getNextPageParam: (lastPage) => lastPage.nextCursor,
-        initialPageParam: null as Date | null,
+        getNextPageParam: (lastPage) => lastPage.nextCursor,
+        initialPageParam: null as string | null,

Also applies to: 51-51

♻️ Duplicate comments (1)
apps/web/src/data/use-conversation-messages.tsx (1)

29-35: LGTM: queryKey now includes limit (prevents cache collisions)

The base key includes limit; this resolves the previously reported collision.

🧹 Nitpick comments (2)
apps/api/src/ws/router.ts (1)

33-44: Replace console. with structured logger*

Repository guidelines disallow console.*. Use a shared logger (e.g., pino/winston) or your existing logging util, with consistent levels and metadata.

Example (illustrative):

- console.log(`[USER_CONNECTED] User ${data.userId} connected`, { ... })
+ logger.info({ evt: "USER_CONNECTED", userId: data.userId, ... }, "user connected")

- console.error(`[EventRouter] Error handling ${event.type}:`, error);
+ logger.error({ evt: "EventRouterError", type: event.type, err: error }, "handler error")

Also applies to: 47-58, 61-72, 75-89, 92-105, 108-114, 135-145

apps/web/src/data/use-conversation-messages.tsx (1)

33-33: Use DEFAULT_PAGE_LIMIT for consistency with queryFn

Avoid hardcoded 50 so keys and fetch use the same default.

-                limit: options?.limit ?? 50,
+                limit: options?.limit ?? DEFAULT_PAGE_LIMIT,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a614398 and 1295771.

📒 Files selected for processing (8)
  • apps/api/src/rest/routers/conversation.ts (2 hunks)
  • apps/api/src/rest/routers/messages.ts (2 hunks)
  • apps/api/src/trpc/routers/conversation.ts (1 hunks)
  • apps/api/src/ws/router.test.ts (1 hunks)
  • apps/api/src/ws/router.ts (2 hunks)
  • apps/web/src/components/conversation/index.tsx (2 hunks)
  • apps/web/src/data/use-conversation-messages.tsx (2 hunks)
  • packages/types/src/realtime-events.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/components/conversation/index.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/api/src/rest/routers/conversation.ts
  • apps/api/src/trpc/routers/conversation.ts
  • apps/api/src/ws/router.test.ts
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)

**/*.{ts,tsx}: Manage shared component state via React Context
Implement full TypeScript support for all public APIs
Provide proper type definitions for all props and refs
Use appropriate generic constraints where necessary
Export type definitions for the public API
Avoid TypeScript enums; prefer const objects with 'as const' or Object.freeze
Provide clear, descriptive runtime errors for missing context/props and misuse
Expose dedicated hooks for context access that throw helpful errors when context is missing
Add explicit error messages for: outside-context usage, missing required props, invalid prop combinations, and validation failures
Document each component with JSDoc: purpose, props (required/optional), context requirements, example usage, pitfalls
State management: use React Context for global state, perform correct state updates, and handle side effects appropriately
Validation: implement runtime checks and validate prop combinations with clear errors
Create context with createContext<ContextType|null>(null) and a useContext hook that throws a clear error when missing

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't have unused function parameters
Don't have unused private class members
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Ensure switch-case statements are exhaustive (especially with unions)
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types (where discouraged by policy)
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibili...

Files:

  • apps/api/src/ws/router.ts
  • packages/types/src/realtime-events.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/rest/routers/messages.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use the accessKey attribute on any HTML element
Use correct ISO language/country codes for the lang attribute
Don't hardcode sensitive data like API keys and tokens
Use === and !==

Files:

  • apps/api/src/ws/router.ts
  • packages/types/src/realtime-events.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/rest/routers/messages.ts
**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Prefer for...of statements instead of Array.forEach
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Avoid ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use Date.now() to get milliseconds since the Unix Epoch
Prefer .flatMap() over map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when numeric literals suffice (binary/octal/hex)
Use concise optional chaining instead of chained logical expressions
Prefer regular expression literals over the RegExp constructor when possible
Don't use non–base-10 or underscore-separated number literal object member names
Remove redundant terms from logical expressions
Prefer while loops over for loops when no initializer/update is needed
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min/Math.max to clamp values when result is constant
Don't use empty character classes in regular expression literals
Don't use empty destructuring patterns
Don't call global object properties as functions
Don't declare functions and vars that are acce...

Files:

  • apps/api/src/ws/router.ts
  • packages/types/src/realtime-events.ts
  • apps/api/src/rest/routers/messages.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts,tsx}: Don't create classes that only have static members
Don't return a value from a constructor
Don't use super() incorrectly inside classes; ensure super() is called in derived constructors
Ensure super() is called exactly once on every code path in a class constructor before this is accessed if the class has a superclass
Place getters and setters for the same property next to each other in class and object definitions
Ensure JSDoc comment lines start with a single asterisk, except for the first one
Don't use duplicate class members

Files:

  • apps/api/src/ws/router.ts
  • packages/types/src/realtime-events.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/rest/routers/messages.ts
**/*.{js,tsx,ts,jsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

Use arrow functions instead of function expressions

Files:

  • apps/api/src/ws/router.ts
  • packages/types/src/realtime-events.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/rest/routers/messages.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts,jsx,tsx}: Prevent duplicate polyfills from Polyfill.io
Don't use irregular whitespace characters

Files:

  • apps/api/src/ws/router.ts
  • packages/types/src/realtime-events.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/rest/routers/messages.ts
**/*.{js,ts,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

Don't redeclare variables, functions, classes, and types in the same scope

Files:

  • apps/api/src/ws/router.ts
  • packages/types/src/realtime-events.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/rest/routers/messages.ts
packages/types/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

packages/types/src/**/*.ts: Keep shared TypeScript types and Zod schemas in packages/types/src/ and update them alongside schema changes
Ensure Zod schemas mirror any API/database shape changes for end-to-end type safety

Files:

  • packages/types/src/realtime-events.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)

**/*.tsx: Use the Compound Components pattern with namespaced exports (e.g., FormWizard.Root, FormWizard.Step) so pieces compose under a single parent
Ensure accessibility: keyboard navigation support
Ensure accessibility: include appropriate ARIA attributes
Ensure accessibility: use semantic markup for screen readers
Maintain proper focus management in interactive components
Structure components with forwardRef and typed props/refs as shown (ComponentRef, HTMLAttributes, ReactNode)
Root component acts as main context provider, manages global state, and handles initialization
Child components must be used within Root, read context via hooks, support asChild, and follow consistent props
Props ordering: list required props first; optional props have clear defaults; support common HTML attributes; expose className
Support composition with an asChild prop using Radix UI Slot to allow element replacement
Styling: support className, use data-* attributes for state, and allow style customization
Event handling: support standard DOM events, add custom events when needed, and manage propagation correctly
Base component pattern: forwardRef, choose Slot or intrinsic element via asChild, pass ref/className and spread props

Files:

  • apps/web/src/data/use-conversation-messages.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't set aria-hidden="true" on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like or
Only use the scope prop on elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Ensure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assign tabIndex to non-interactive HTML elements
Don't use positive integers for tabIndex property
Don't include the words "image", "picture", or "photo" in img alt prop
Don't use an explicit role that's the same as the element's implicit role
Static elements with click handlers must have a valid role attribute
Always include a <title> element within SVG elements
Provide meaningful alt text for elements that require alt text
Ensure anchors have content accessible to screen readers
Assign tabIndex to non-interactive elements only when using aria-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Ensure ARIA properties are valid for the element's supported roles
Always include a type attribute for button elements
Elements with interactive roles and handlers must be focusable
Heading elements must have content accessible to screen readers (not hidden with aria-hidden)
Always include a lang attribute on the html element
Always include a title attribute for iframe elements
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress
Accompany onMouseOver/onMouseOut with onFocus/onBlur
Include caption tracks for audio and video elements
Prefer semantic elements instead of role attributes in JSX
Ensure all anchors are valid and navigable
Ensure all aria-* properties are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid ARIA state and property values
Use valid values for the autocomplete attribute on input elements
Don't use unnecessary fragmen...

Files:

  • apps/web/src/data/use-conversation-messages.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx,html}: Ensure the preconnect attribute is used when using Google Fonts
Don't use target="_blank" without rel="noopener"
Use a recommended display strategy with Google Fonts

Files:

  • apps/web/src/data/use-conversation-messages.tsx
🧬 Code graph analysis (3)
apps/api/src/ws/router.ts (2)
packages/types/src/realtime-events.ts (3)
  • RealtimeEventType (36-36)
  • RealtimeEventData (44-46)
  • RealtimeEvent (38-42)
apps/api/src/lib/pubsub.ts (2)
  • emitToDashboard (584-590)
  • emitToAll (592-599)
apps/web/src/data/use-conversation-messages.tsx (1)
apps/web/src/data/conversation-message-cache.ts (1)
  • createConversationMessagesInfiniteQueryKey (112-116)
apps/api/src/rest/routers/messages.ts (4)
packages/types/src/api/message.ts (4)
  • getMessagesRequestSchema (5-9)
  • getMessagesResponseSchema (14-18)
  • sendMessageRequestSchema (23-35)
  • sendMessageResponseSchema (40-42)
apps/api/src/utils/validate.ts (3)
  • safelyExtractRequestQuery (120-162)
  • validateResponse (6-20)
  • safelyExtractRequestData (56-107)
apps/api/src/db/queries/message.ts (1)
  • getMessages (49-96)
apps/api/src/utils/message.ts (1)
  • createMessage (68-112)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (2)
apps/api/src/rest/routers/messages.ts (1)

204-223: LGTM: route delegates to createMessage and validates response

Creation flows through a single utility and response is schema-validated. Good.

apps/api/src/ws/router.ts (1)

116-121: ```shell
#!/bin/bash
set -euo pipefail

echo "PWD: $(pwd)"
echo

echo "---- apps/api/src/ws/router.ts (first 240 lines) ----"
if [ -f apps/api/src/ws/router.ts ]; then
cat -n apps/api/src/ws/router.ts | sed -n '1,240p'
else
echo "MISSING: apps/api/src/ws/router.ts"
fi

echo
echo "---- apps/api/src/lib/pubsub.ts (lines 540-720) ----"
if [ -f apps/api/src/lib/pubsub.ts ]; then
rg -n "emitToAll" apps/api/src/lib/pubsub.ts || true
cat -n apps/api/src/lib/pubsub.ts | sed -n '540,720p'
else
echo "MISSING: apps/api/src/lib/pubsub.ts"
fi

echo
echo "---- apps/api/src/utils/message.ts (first 240 lines) ----"
if [ -f apps/api/src/utils/message.ts ]; then
cat -n apps/api/src/utils/message.ts | sed -n '1,240p'
else
echo "MISSING: apps/api/src/utils/message.ts"
fi

echo
echo "---- Search for routeEvent definition ----"
rg -n --hidden "function\s+routeEvent|export\s+function\s+routeEvent|const\s+routeEvent\s*[:=]" || true

echo
echo "---- Search for any code that forwards Pub/Sub messages into routeEvent or websocket handlers ----"
rg -n --hidden "routeEvent\(|subscribe\(|onMessage\(|handleMessage\(|deliver\(|fromPubSub|pubsub" apps || true

echo
echo "---- Search for 'source' field usage in event objects ----"
rg -n --hidden ""source"\s*[:=]|source:\s*" || true


</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread apps/api/src/rest/routers/messages.ts Outdated
Comment on lines 195 to 203
if (!visitorId) {
return c.json(
validateResponse(
{ error: "Visitor ID is required" },
z.object({ error: z.string() })
)
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Return proper 400 for missing Visitor ID (don’t respond 200 with error body)

Current code returns 200 with an error payload. Throw an HTTPException 400 for consistent error handling and OpenAPI semantics.

-    if (!visitorId) {
-      return c.json(
-        validateResponse(
-          { error: "Visitor ID is required" },
-          z.object({ error: z.string() })
-        )
-      );
-    }
+    if (!visitorId) {
+      throw new HTTPException(400, { message: "Visitor ID is required" });
+    }

And add import:

 import { OpenAPIHono, z } from "@hono/zod-openapi";
+import { HTTPException } from "hono/http-exception";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!visitorId) {
return c.json(
validateResponse(
{ error: "Visitor ID is required" },
z.object({ error: z.string() })
)
);
}
import { OpenAPIHono, z } from "@hono/zod-openapi";
import { HTTPException } from "hono/http-exception";
if (!visitorId) {
throw new HTTPException(400, { message: "Visitor ID is required" });
}
🤖 Prompt for AI Agents
In apps/api/src/rest/routers/messages.ts around lines 195 to 203, the handler
currently returns a 200 JSON payload containing an error object when visitorId
is missing; change this to throw an HTTPException with status 400 instead.
Replace the c.json(validateResponse(...)) return with: throw new
HTTPException(400, validateResponse({ error: "Visitor ID is required" },
z.object({ error: z.string() }))); and add the necessary import for
HTTPException at the top of the file (import { HTTPException } from the
framework/bootstrap module used in this codebase). Ensure no other code path
expects the old 200 response.

Comment thread apps/api/src/ws/router.ts Outdated
@@ -1,63 +1,64 @@
import { z } from "zod";
import { MessageType, MessageVisibility } from "./enums";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add MESSAGE_CREATED schema and remove unused imports

The WS/router and utils emit MESSAGE_CREATED, but this file doesn’t define it. Also, MessageType/MessageVisibility are imported but unused.

Apply:

-import { MessageType, MessageVisibility } from "./enums";
+import { messageSchema } from "./api/message";
 export const RealtimeEvents = {
   USER_CONNECTED: z.object({
     userId: z.string(),
     connectionId: z.string(),
     timestamp: z.number(),
   }),
   USER_DISCONNECTED: z.object({
     userId: z.string(),
     connectionId: z.string(),
     timestamp: z.number(),
   }),
   VISITOR_CONNECTED: z.object({
     visitorId: z.string(),
     connectionId: z.string(),
     timestamp: z.number(),
   }),
   VISITOR_DISCONNECTED: z.object({
     visitorId: z.string(),
     connectionId: z.string(),
     timestamp: z.number(),
   }),
   USER_PRESENCE_UPDATE: z.object({
     userId: z.string(),
     status: z.enum(["online", "away", "offline"]),
     lastSeen: z.number(),
   }),
+  MESSAGE_CREATED: z.object({
+    conversationId: z.string(),
+    websiteId: z.string(),
+    organizationId: z.string(),
+    // Align with serializeMessageForRealtime; adjust if that serializer changes.
+    message: messageSchema,
+  }),
 } as const;

If createdAt is serialized (e.g., to ISO string/number), refine messageSchema here or introduce a dedicated messageRealtimeSchema to match the wire format.

Also applies to: 9-34

🤖 Prompt for AI Agents
In packages/types/src/realtime-events.ts around lines 2 and 9-34, the file
currently imports MessageType and MessageVisibility but doesn't use them and
lacks a MESSAGE_CREATED event schema — add a MESSAGE_CREATED schema (or
messageRealtimeSchema) describing the wire format emitted by WS/router and
utils, remove the unused MessageType/MessageVisibility imports, and ensure the
schema reflects how createdAt is serialized on the wire (if createdAt is sent as
ISO string or epoch number, adjust the field type accordingly or create a
dedicated messageRealtimeSchema matching the serialized format).

parentMessageId: z.string().nullable(),
aiAgentId: z.string().nullable(),
modelUsed: z.string().nullable(),
visibility: z.string(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Event Schema Validation Flaw

The MESSAGE_CREATED event schema defines type and visibility as z.string(). This allows invalid values to pass validation, creating a type safety issue. The imported MessageType and MessageVisibility enums could be used to properly constrain these fields, consistent with other message schemas.

Fix in Cursor Fix in Web

@Rieranthony
Rieranthony merged commit a2d46fe into main Sep 17, 2025
2 of 6 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/src/rest/routers/conversation.ts (1)

404-416: Remove debug logging of headers (secrets leak) and console usage.

This logs Authorization/Public keys to stdout. Remove or guard behind a debug flag and redact values. Also aligns with the “Don’t use console” guideline.

-    // Debug: Check what's in the context
-    console.log("[DEBUG] GET /conversations/{id} - Context check:", {
-      hasDb: !!c.get("db"),
-      hasWebsite: !!c.get("website"),
-      hasOrganization: !!c.get("organization"),
-      hasApiKey: !!c.get("apiKey"),
-      headers: {
-        authorization: c.req.header("Authorization"),
-        publicKey: c.req.header("X-Public-Key"),
-        visitorId: c.req.header("X-Visitor-Id"),
-      },
-    });
+    // Consider using a structured logger at debug level without sensitive headers if needed.
♻️ Duplicate comments (3)
apps/api/src/rest/routers/messages.ts (1)

195-202: Return 400 via HTTPException for missing Visitor ID (don’t 200-with-error).

Aligns with REST semantics and prior guidance.

-    if (!visitorId) {
-      return c.json(
-        validateResponse(
-          { error: "Visitor ID is required" },
-          z.object({ error: z.string() })
-        )
-      );
-    }
+    if (!visitorId) {
+      throw new HTTPException(400, { message: "Visitor ID is required" });
+    }

Add the import:

-import { OpenAPIHono, z } from "@hono/zod-openapi";
+import { OpenAPIHono, z } from "@hono/zod-openapi";
+import { HTTPException } from "hono/http-exception";
apps/api/src/rest/routers/conversation.ts (1)

143-163: Guarding defaultMessages and per-message creation look good; consider atomicity.

Nice normalization via defaults = body.defaultMessages ?? []. However, concurrent per-message inserts can leave partial state if one fails; wrap in a DB transaction and emit events after commit.

Example (pseudo-code):

await db.transaction(async (trx) => {
  const results = [];
  for (const msg of defaults) {
    results.push(
      await createMessage({ db: trx, organizationId, websiteId, conversationId, message: {/*...*/} })
    );
  }
  return results;
}); // after commit: emit/broadcast if needed
apps/api/src/ws/router.test.ts (1)

5-12: Export emitToAll from the pubsub mock (tests currently fail).

The mock module doesn’t export emitToAll, so the router uses the real function and your expectations see 0 calls. Export it alongside emitToDashboard.

Apply this diff:

-const emitToAll = mock(async () => {});
+const emitToAll = mock(async () => {});
 
 mock.module("@api/lib/pubsub", () => ({
-  emitToDashboard,
+  emitToDashboard,
+  emitToAll,
   pubsub: {
     publish: async () => {},
   },
 }));
🧹 Nitpick comments (3)
apps/api/src/ws/router.test.ts (1)

17-19: Reset all mocks between tests.

Add emitToAll.mockReset() in beforeEach to avoid cross-test leakage.

 beforeEach(() => {
   emitToDashboard.mockReset();
+  emitToAll.mockReset();
 });
apps/api/src/rest/routers/messages.ts (1)

204-218: Creation flow LGTM; defaults delegated to createMessage.

Mapping to createMessage is correct; undefined values fall back to TEXT/PUBLIC inside createMessage. Consider accepting optional parentMessageId/modelUsed when available so API parity matches the Message shape end-to-end.

apps/api/src/rest/routers/conversation.ts (1)

76-101: Gitleaks false positive: obfuscate token examples in OpenAPI.

Examples currently match Stripe key patterns and trip scanners. Use obviously fake placeholders that don’t match the regex.

Example replacements:

  • Authorization example: "Bearer sk_test_not_a_real_token_example"
  • X-Public-Key example: "pk_test_not_a_real_public_key"

Also applies to: 236-251, 371-389

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1295771 and c7901f6.

📒 Files selected for processing (12)
  • apps/api/src/rest/routers/conversation.ts (2 hunks)
  • apps/api/src/rest/routers/messages.ts (2 hunks)
  • apps/api/src/trpc/routers/conversation.ts (2 hunks)
  • apps/api/src/utils/message.ts (1 hunks)
  • apps/api/src/ws/router.test.ts (1 hunks)
  • apps/api/src/ws/router.ts (2 hunks)
  • apps/web/src/app/(lander-docs)/components/docs/mdx-components.tsx (1 hunks)
  • apps/web/src/components/conversation/index.tsx (1 hunks)
  • apps/web/src/data/conversation-message-cache.ts (1 hunks)
  • apps/web/src/data/use-conversation-messages.tsx (2 hunks)
  • packages/types/src/realtime-events.ts (1 hunks)
  • packages/types/src/schemas.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/app/(lander-docs)/components/docs/mdx-components.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/web/src/data/conversation-message-cache.ts
  • apps/web/src/components/conversation/index.tsx
  • apps/api/src/utils/message.ts
  • apps/api/src/ws/router.ts
  • apps/web/src/data/use-conversation-messages.tsx
  • apps/api/src/trpc/routers/conversation.ts
  • packages/types/src/realtime-events.ts
🧰 Additional context used
📓 Path-based instructions (10)
packages/types/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

packages/types/src/**/*.ts: Keep shared TypeScript types and Zod schemas in packages/types/src/ and update them alongside schema changes
Ensure Zod schemas mirror any API/database shape changes for end-to-end type safety

Files:

  • packages/types/src/schemas.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)

**/*.{ts,tsx}: Manage shared component state via React Context
Implement full TypeScript support for all public APIs
Provide proper type definitions for all props and refs
Use appropriate generic constraints where necessary
Export type definitions for the public API
Avoid TypeScript enums; prefer const objects with 'as const' or Object.freeze
Provide clear, descriptive runtime errors for missing context/props and misuse
Expose dedicated hooks for context access that throw helpful errors when context is missing
Add explicit error messages for: outside-context usage, missing required props, invalid prop combinations, and validation failures
Document each component with JSDoc: purpose, props (required/optional), context requirements, example usage, pitfalls
State management: use React Context for global state, perform correct state updates, and handle side effects appropriately
Validation: implement runtime checks and validate prop combinations with clear errors
Create context with createContext<ContextType|null>(null) and a useContext hook that throws a clear error when missing

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't have unused function parameters
Don't have unused private class members
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Ensure switch-case statements are exhaustive (especially with unions)
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types (where discouraged by policy)
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibili...

Files:

  • packages/types/src/schemas.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/api/src/ws/router.test.ts
  • apps/api/src/rest/routers/conversation.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use the accessKey attribute on any HTML element
Use correct ISO language/country codes for the lang attribute
Don't hardcode sensitive data like API keys and tokens
Use === and !==

Files:

  • packages/types/src/schemas.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/api/src/ws/router.test.ts
  • apps/api/src/rest/routers/conversation.ts
**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Prefer for...of statements instead of Array.forEach
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Avoid ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use Date.now() to get milliseconds since the Unix Epoch
Prefer .flatMap() over map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when numeric literals suffice (binary/octal/hex)
Use concise optional chaining instead of chained logical expressions
Prefer regular expression literals over the RegExp constructor when possible
Don't use non–base-10 or underscore-separated number literal object member names
Remove redundant terms from logical expressions
Prefer while loops over for loops when no initializer/update is needed
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min/Math.max to clamp values when result is constant
Don't use empty character classes in regular expression literals
Don't use empty destructuring patterns
Don't call global object properties as functions
Don't declare functions and vars that are acce...

Files:

  • packages/types/src/schemas.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/api/src/ws/router.test.ts
  • apps/api/src/rest/routers/conversation.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts,tsx}: Don't create classes that only have static members
Don't return a value from a constructor
Don't use super() incorrectly inside classes; ensure super() is called in derived constructors
Ensure super() is called exactly once on every code path in a class constructor before this is accessed if the class has a superclass
Place getters and setters for the same property next to each other in class and object definitions
Ensure JSDoc comment lines start with a single asterisk, except for the first one
Don't use duplicate class members

Files:

  • packages/types/src/schemas.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/api/src/ws/router.test.ts
  • apps/api/src/rest/routers/conversation.ts
**/*.{js,tsx,ts,jsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

Use arrow functions instead of function expressions

Files:

  • packages/types/src/schemas.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/api/src/ws/router.test.ts
  • apps/api/src/rest/routers/conversation.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts,jsx,tsx}: Prevent duplicate polyfills from Polyfill.io
Don't use irregular whitespace characters

Files:

  • packages/types/src/schemas.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/api/src/ws/router.test.ts
  • apps/api/src/rest/routers/conversation.ts
**/*.{js,ts,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

Don't redeclare variables, functions, classes, and types in the same scope

Files:

  • packages/types/src/schemas.ts
  • apps/api/src/rest/routers/messages.ts
  • apps/api/src/ws/router.test.ts
  • apps/api/src/rest/routers/conversation.ts
**/*.{test.ts,test.tsx,spec.ts,spec.tsx,test.js,test.jsx,spec.js,spec.jsx}

📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)

Include tests: unit tests for components, integration tests for composed components, accessibility tests, and error boundary tests

Files:

  • apps/api/src/ws/router.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Ensure the assertion function (e.g., expect) is placed inside an it() function call
Don't use disabled tests

Files:

  • apps/api/src/ws/router.test.ts
🧬 Code graph analysis (4)
packages/types/src/schemas.ts (1)
packages/react/src/provider.tsx (1)
  • DefaultMessage (28-32)
apps/api/src/rest/routers/messages.ts (1)
apps/api/src/utils/message.ts (1)
  • createMessage (70-109)
apps/api/src/ws/router.test.ts (3)
apps/api/src/lib/pubsub.ts (2)
  • emitToAll (592-599)
  • emitToDashboard (584-590)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (60-64)
apps/api/src/ws/router.ts (1)
  • routeEvent (128-146)
apps/api/src/rest/routers/conversation.ts (1)
apps/api/src/utils/message.ts (1)
  • createMessage (70-109)
🪛 GitHub Check: API Tests
apps/api/src/ws/router.test.ts

[failure] 115-115: error: expect(received).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0

  at <anonymous> (/home/runner/work/cossistant/cossistant/apps/api/src/ws/router.test.ts:115:23)

[failure] 69-69: error: expect(received).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0

  at <anonymous> (/home/runner/work/cossistant/cossistant/apps/api/src/ws/router.test.ts:69:29)

[failure] 41-41: error: expect(received).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0

  at <anonymous> (/home/runner/work/cossistant/cossistant/apps/api/src/ws/router.test.ts:41:29)
🪛 GitHub Actions: PR Tests
apps/api/src/ws/router.test.ts

[error] 1-1: Expected number of calls: 1

🪛 Gitleaks (8.27.2)
apps/api/src/rest/routers/conversation.ts

[high] 160-160: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.

(stripe-access-token)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (5)
packages/types/src/schemas.ts (1)

15-16: Schema extension looks good; confirm storage and event parity.

parentMessageId/modelUsed as nullable strings are fine. Please verify:

  • DB layer has matching nullable columns and serializers.
  • Realtime payload serializers include these fields consistently with the MESSAGE_CREATED event.
apps/api/src/ws/router.test.ts (3)

21-47: Why 0 calls? Double-check handler wiring for USER_PRESENCE_UPDATE.

Once the pubsub mock is fixed, this should pass. If it still fails, verify the event handler actually calls emitToDashboard for visitor-originated presence updates.


49-75: VISITOR_CONNECTED test depends on the same mock fix.

After exporting emitToAll/emitToDashboard in the mock, this should pass. If not, re-check the handler path.


78-123: Realtime date shape: ensure test payload matches event schema.

createdAt/updatedAt are ISO strings here. Confirm RealtimeEvents expect strings (not Date objects). If Dates are expected, switch to new Date() instead of toISOString().

apps/api/src/rest/routers/conversation.ts (1)

7-7: Right import.

Switch to createMessage aligns with the new per-message flow.

This branch had an error being deployed

1 failed deployment
Preview — c7901f66 Deployed Sep 17, 2025 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant