Skip to content

feat(react): align support hooks with client query - #14

Merged
Rieranthony merged 9 commits into
mainfrom
codex/migrate-state-to-@cossistant/core-store
Sep 28, 2025
Merged

Rieranthony merged 9 commits into
mainfrom
codex/migrate-state-to-@cossistant/core-store

Conversation

@Rieranthony

@Rieranthony Rieranthony commented Sep 27, 2025 •

Copy link
Copy Markdown
Contributor

Summary

  • update conversation message, conversation list, and single conversation hooks to fetch via useClientQuery while reading from the core stores
  • add a website convenience hook and re-export the new hook option/result types from the react entrypoint
  • simplify support pages to consume the refreshed hooks without passing the client directly

Testing

  • bun test packages/core/src/store/messages-store.test.ts (fails: Cannot find package 'nanoid' from '/workspace/cossistant/packages/core/src/utils.ts')

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

Summary by CodeRabbit

  • New Features

    • Client-side stores for conversations, messages, and website with optimistic updates and realtime ingestion; client APIs to fetch/handle website, initiate conversations, and ingest realtime events.
    • New React/Next hooks for conversations, messages, create/send flows, and client-backed queries.
  • Documentation

    • Contribution Playbook added detailing conventions, store patterns, testing checklist, and workflows.
  • Tests

    • Comprehensive tests for conversations, messages, and website stores with client integration.
  • Refactor

    • Consolidated/re-exported hooks and simplified provider/realtime wiring.
  • Chores

    • Linter/config tweaks and formatting cleanups.

Note

Introduce core client stores with optimistic/realtime updates and refactor React/Next hooks and provider to fetch via client + stores, simplifying consumers.

  • Core (@cossistant/core):
    • Client Stores: Add conversations, messages, and website stores with immutable reducers, selectors, and tests; hydrate via REST and ingest realtime MESSAGE_CREATED.
    • Client API: CossistantClient now hydrates stores (list/get/create, messages, website fetch with caching), supports optimistic sendMessage and initiateConversation for pending convos.
    • REST Client: Expose current website/visitor getters; minor validations; mark-seen flow tightened.
    • Docs/Utils: Add detailed LLM contribution guide; base store primitive.
  • React (@cossistant/react):
    • Hooks: New useClientQuery, useWebsiteStore, useConversations, useConversation, useConversationMessages, useCreateConversation, useSendMessage; unify over core client + stores; re-export types.
    • Provider: Uses website store; useClient now throws on missing key and returns non-null client.
    • Realtime: Bridge socket events directly into core stores.
    • Primitives: Minor type/forwardRef cleanups.
  • Next (@cossistant/next):
    • Re-export new React hooks/provider; add provider entry and hook facades.
  • Web (apps/web):
    • Update dashboard realtime context (inject QueryClient); tweak CONVERSATION_SEEN handler; fix imports; small UI adjustments.
  • API:
    • Minor formatting/ignore cleanup; tests adjusted; realtime schema/types expanded.
  • Types:
    • Add realtime event types for conversation seen/typing/event-created.
  • Tooling:
    • Biome config: relax some complexity rules.

Written by Cursor Bugbot for commit 0ce55e7. This will update automatically on new commits. Configure here.

@vercel

vercel Bot commented Sep 27, 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 28, 2025 5:44pm

@coderabbitai

coderabbitai Bot commented Sep 27, 2025 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

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

Adds a generic in-memory store system and domain stores (conversations, messages, website) to @cossistant/core, integrates them into CossistantClient with optimistic and realtime flows, and migrates React/Next hooks and provider code to use store-backed querying; includes tests and API/event schema additions.

Changes

Cohort / File(s) Change summary
Core: Stores & Store infra
packages/core/src/store/create-store.ts, packages/core/src/store/conversations-store.ts, packages/core/src/store/messages-store.ts, packages/core/src/store/website-store.ts
Add generic createStore and three domain stores with selectors, ingest/merge logic, batching, change-guards, optimistic helpers and realtime ingestion hooks.
Core: Client & REST
packages/core/src/client.ts, packages/core/src/rest-client.ts, packages/core/src/index.ts
Client now instantiates and exposes stores, adds fetchWebsite, initiateConversation, handleRealtimeEvent, pending-conversation support, optimistic sendMessage integration; REST client gains getCurrentWebsiteId/getCurrentVisitorId; index re-exports store APIs.
Core: Docs
packages/core/LLM_GUIDE.md
Adds contribution playbook detailing store/layout/naming/workflows/testing and transport/event mapping.
React: Store hooks & selector utils
packages/react/src/hooks/use-client-query.ts, packages/react/src/hooks/store/use-store-selector.ts, packages/react/src/hooks/store/use-website-store.ts, packages/react/src/hooks/store/use-conversations-store.ts
Introduce useClientQuery and useStoreSelector plus hooks to select website and conversation data from stores with refetch/refresh policies.
React: Feature hooks
packages/react/src/hooks/use-conversations.ts, .../use-conversation.ts, .../use-conversation-messages.ts, .../use-create-conversation.ts, .../use-send-message.ts
New hooks for conversations and paginated messages, createConversation and sendMessage flows (including optimistic / pending conversation handling).
React: Provider & Realtime
packages/react/src/provider.tsx, packages/react/src/realtime/index.ts, packages/react/src/realtime/support-provider.tsx
Provider sources website via store; realtime context now carries client and forwards events to client.handleRealtimeEvent; removes direct QueryClient dependency from realtime types/handlers.
React: Public barrel exports & re-exports
packages/react/src/index.ts, packages/react/src/hooks/index.ts, packages/react/src/support/hooks/index.ts, packages/react/src/support/hooks/use-* (re-exports)
Centralize and re-export new hooks/types; many support modules now re-export canonical hook implementations.
React: Hook removals / legacy cleanup
packages/react/src/support/hooks/use-website-data.ts, packages/react/src/support/hooks/use-messages.ts, packages/react/src/utils/message-converter.ts
Remove react-query-based website/messages hooks and message-converter utilities (replaced by store-backed flows).
React: Type/prop typing cleanup
multiple packages/react/src/primitives/*, packages/react/src/config.tsx, packages/react/src/support/*
Replace local Props aliases with public prop types, rename some exported prop types, and adjust hook signatures/types (including useClientResult client non-null).
Next: Client re-export shims
packages/next/src/hooks/*, packages/next/src/index.ts, packages/next/src/provider.tsx, packages/next/src/hooks/use-rest-client.ts
Add client-side re-export wrappers for new hooks and provider, expose some types.
API: Events & emit helpers
apps/api/src/utils/conversation-realtime.ts, apps/api/src/rest/routers/conversation.ts, apps/api/.../visitor.ts, apps/api/src/ws/*
Convert import to type-only, remove emitConversation* helpers, adjust event payloads (omit visitorId in some events), formatting and lint comment removals.
Types: Realtime events
packages/types/src/realtime-events.ts, packages/types/src/api/conversation.ts
Add CONVERSATION_SEEN, CONVERSATION_TYPING, CONVERSATION_EVENT_CREATED event schemas; minor formatting change in API schema.
Web app: Consumers & wiring
apps/web/src/components/conversation/*, apps/web/src/app/(dashboard)/**, apps/web/src/app/playground/page.tsx, apps/web/src/lib/*
Migrate UI to new hooks (useConversationMessages/useSendMessage/useConversations), adjust seen handler condition and realtime context wiring; mostly formatting and import updates.
Tests: Stores & client integration
packages/core/src/store/conversations-store.test.ts, .../messages-store.test.ts, .../website-store.test.ts
Add comprehensive tests for stores and client interactions covering ingest/merge/pagination/optimistic/realtime and website caching flows.
Config / lint meta
biome.jsonc, apps/api/scripts/migrate.ts, apps/api/src/db/cache/bun-redis-cache.ts, apps/web/src/lib/trpc/server.tsx
Add linter complexity toggles and remove several lint-suppression comments.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor UI
  participant ReactHook as useSendMessage
  participant Client as CossistantClient
  participant Stores as {conversations|messages}
  participant REST as API

  UI->>ReactHook: mutate({ conversationId?, bodyMd, defaultMessages? })
  alt No conversationId (new)
    ReactHook->>Client: initiateConversation({ defaultMessages })
    Client->>Stores: conversationsStore.ingestConversation(pending)
    Client-->>ReactHook: { conversationId, initialMessages? }
  end
  ReactHook->>Client: sendMessage({ conversationId, bodyMd, createIfPending: true })
  Client->>Stores: messagesStore.ingestMessage(optimistic)
  Client->>REST: POST /messages
  alt Success
    REST-->>Client: { message, conversation? }
    Client->>Stores: messagesStore.finalizeMessage(...)
    Client->>Stores: conversationsStore.ingestConversation(updated?)
    ReactHook-->>UI: { conversationId, messageId, conversation? }
  else Failure
    Client->>Stores: messagesStore.removeMessage(optimisticId)
    ReactHook-->>UI: throw Error
  end
Loading
sequenceDiagram
  autonumber
  actor WS as Realtime
  participant Client as CossistantClient
  participant Stores as {messages|conversations}
  note over WS,Client: MESSAGE_CREATED / CONVERSATION_* events
  WS-->>Client: RealtimeEvent
  Client->>Client: handleRealtimeEvent(event)
  alt MESSAGE_CREATED
    Client->>Stores: messagesStore.ingestRealtime(event)
  else CONVERSATION_SEEN / TYPING / EVENT_CREATED
    Client->>Stores: conversationsStore.ingestConversation/merge(...)
  end
Loading
sequenceDiagram
  autonumber
  actor UI
  participant Hook as useWebsiteStore
  participant Client as CossistantClient
  participant Store as websiteStore
  participant REST as API

  UI->>Hook: mount()
  Hook->>Store: select state
  alt state.status == idle
    Hook->>Client: fetchWebsite()
    Client->>Store: setLoading()
    Client->>REST: GET /website
    alt Success
      REST-->>Client: website
      Client->>Store: setWebsite(website)
    else Failure
      Client->>Store: setError(err)
    end
  else Cached
    Hook-->>UI: website from store
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

A whisk of wires, a hop through state,
I stash our chats, I paginate.
Real-time breezes twitch my ear—
I fold events and keep them near.
New talks bloom with hopeful grace,
Cached websites in a cozy place. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.22% 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 accurately captures the key React change—aligning support hooks with the new client query mechanism—but only reflects one facet of a larger, multi-package migration that also includes core store integration, REST client enhancements, tooling updates, and more.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56b2ac5 and 0ce55e7.

📒 Files selected for processing (1)
  • apps/web/src/components/conversation/messages/read-indicator.tsx (1 hunks)

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: 10

Caution

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

⚠️ Outside diff range comments (2)
packages/react/src/support/pages/home.tsx (1)

111-139: Defensive check for optional quickOptions

quickOptions.length will throw if quickOptions is undefined. Use a nullish-safe length.

-					{quickOptions.length > 0 && (
+					{(quickOptions?.length ?? 0) > 0 && (
packages/core/src/rest-client.ts (1)

27-27: Add missing nanoid dependency to core package
The utils module in packages/core/src/utils.ts imports customAlphabet from nanoid, causing build/tests to fail with “Cannot find package 'nanoid'”. Add the following under dependencies in packages/core/package.json to restore the build:

--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -5,6 +5,9 @@
   "license": "MIT",
   "type": "module",
   "exports": { … },
+  "dependencies": {
+    "nanoid": "^5.0.7"
+  }
 }

(Optional long-term) Replace nanoid with a Web Crypto–based ID generator to remove this dependency.

🧹 Nitpick comments (27)
packages/react/src/hooks/use-store-selector.ts (1)

1-29: Stabilize the subscribe/getSnapshot callbacks passed to useSyncExternalStore

Both subscribe and the inline () => store.getState() closures are re-created every render. useSyncExternalStore treats changes in either callback as a signal to tear down and recreate the subscription, so we end up unsubscribing and resubscribing on every render even when nothing changed. This adds avoidable churn to the store listeners. Capture them in useCallback (or hoist stable references) so the identities stay fixed across renders.

-import { useRef, useSyncExternalStore } from "react";
+import { useCallback, useRef, useSyncExternalStore } from "react";
@@
-	const subscribe = (onStoreChange: () => void) =>
-		store.subscribe(() => {
-			onStoreChange();
-		});
-
-	const snapshot = useSyncExternalStore(subscribe, () => store.getState(), () => store.getState());
+	const subscribe = useCallback(
+		(onStoreChange: () => void) =>
+			store.subscribe(() => {
+				onStoreChange();
+			}),
+		[store]
+	);
+	const getSnapshot = useCallback(() => store.getState(), [store]);
+
+	const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
packages/core/LLM_GUIDE.md (1)

7-26: Add a language hint to the fenced directory tree

markdownlint (MD040) complains about the bare triple backticks here. Annotating the block (e.g., with text) keeps the lint job green without changing the rendered output.

-```
+```text
 packages/core/
 ├── package.json          # workspace metadata
@@
-```
+```

Based on static analysis hints

packages/react/src/support/hooks/index.ts (2)

2-6: Prefer export type { ... } for type-only re-exports

Align with our TS guideline: split value and type exports for clearer ESM tree-shaking.

Apply:

-export {
-        type ConversationEventItem,
-        type GroupedMessage,
-        type UseGroupedMessagesProps,
-        useGroupedMessages,
-} from "../../hooks/use-grouped-messages";
+export type {
+        ConversationEventItem,
+        GroupedMessage,
+        UseGroupedMessagesProps,
+} from "../../hooks/use-grouped-messages";
+export { useGroupedMessages } from "../../hooks/use-grouped-messages";

8-8: Re-export the hook’s public types alongside the hook

If these types are part of the public surface, re-export them here for discoverability (and also at the package entrypoint).

-export { useWebsite } from "./use-website";
+export { useWebsite } from "./use-website";
+export type { UseWebsiteOptions, UseWebsiteResult } from "./use-website";
packages/react/src/support/pages/home.tsx (2)

45-61: Remove console logs (repo guideline: no console)

Drop debug logs or guard behind a dev-only logger to satisfy lint rules.

-		console.log(
-			"[HomePage] Conversations:",
-			conversations?.map((c) => ({
-				id: c.id,
-				status: c.status,
-				updatedAt: c.updatedAt,
-			}))
-		);
+		// debug: inspect conversations via devtools or a scoped logger if needed
...
-		console.log(
-			"[HomePage] Last open conversation:",
-			_lastOpenConversation?.id
-		);

1-3: Minor: remove unused type-only import

import type React from "react"; isn’t used with the automatic JSX runtime.

-import type React from "react";
packages/core/src/rest-client.ts (1)

227-237: Cache resolved visitor ID to avoid repeated storage lookups

Set this.visitorId when deriving from storage.

-        getCurrentVisitorId(): string | null {
-                if (this.visitorId) {
-                        return this.visitorId;
-                }
-                if (!this.websiteId) {
-                        return null;
-                }
-                return getVisitorId(this.websiteId) ?? null;
-        }
+        getCurrentVisitorId(): string | null {
+                if (this.visitorId) return this.visitorId;
+                if (!this.websiteId) return null;
+                const stored = getVisitorId(this.websiteId);
+                if (stored) this.visitorId = stored;
+                return stored ?? null;
+        }
packages/react/src/support/hooks/use-website.ts (2)

1-12: Add JSDoc for public hook and types

Document purpose, options, defaults, and error states to meet our React package docs guideline.

+/**
+ * useWebsite
+ * Returns website data from the core website store and keeps it refreshed via useClientQuery.
+ * @param options.refetchInterval - ms or false to disable (default: undefined -> store default)
+ * @param options.refetchOnWindowFocus - default true
+ * @returns {UseWebsiteResult} website, status, isLoading, error, refresh()
+ */

1-8: Optional: re-export types instead of aliasing

You can re-export the store types to avoid indirection.

-export type UseWebsiteOptions = UseWebsiteStoreOptions;
-export type UseWebsiteResult = UseWebsiteStoreResult;
+export type { UseWebsiteStoreOptions as UseWebsiteOptions, UseWebsiteStoreResult as UseWebsiteResult } from "../../hooks/use-website-store";
packages/core/src/store/website-store.test.ts (2)

106-127: Add a concurrency test for in‑flight request de‑duplication

You validate cache after the first resolve; also assert coalescing when two fetchWebsite calls start before the first resolves.

Example test to append:

+        it("de-duplicates concurrent in-flight fetches", async () => {
+                const website = createWebsite({ id: "site-concurrent" });
+                let resolver!: (v: unknown) => void;
+                let fetchCount = 0;
+
+                // @ts-expect-error override private for test
+                client.restClient = {
+                        getWebsite: () =>
+                                new Promise<PublicWebsiteResponse>((resolve) => {
+                                        fetchCount += 1;
+                                        resolver = resolve;
+                                }),
+                };
+
+                const p1 = client.fetchWebsite();
+                const p2 = client.fetchWebsite();
+                expect(fetchCount).toBe(1);
+
+                resolver(website);
+                const [r1, r2] = await Promise.all([p1, p2]);
+                expect(r1).toEqual(website);
+                expect(r2).toEqual(website);
+        });

129-154: Good error → recovery flow; consider asserting request handle reset

After a failure and forced refetch, also assert websiteRequest was cleared to avoid stale handles.

Append:

+                // @ts-expect-error private field inspection for test
+                expect(client.websiteRequest).toBeNull();
packages/react/src/hooks/use-website-store.ts (2)

46-49: Expose refetchOnMount in options and thread it through

Let consumers opt-in to mount refetch even when state isn’t idle (e.g., after an error).

 export type UseWebsiteStoreOptions = {
   refetchInterval?: number | false;
   refetchOnWindowFocus?: boolean;
+  refetchOnMount?: boolean;
 };

 ...
-    refetchOnMount: state.status === "idle",
+    refetchOnMount: options.refetchOnMount ?? state.status === "idle",

Also applies to: 75-79


63-103: Add minimal JSDoc for public hook and types

Per guidelines, document purpose, params, return shape, and edge cases (client=null).

As per coding guidelines

packages/core/src/store/create-store.ts (1)

20-26: Isolate subscriber failures.

One listener throwing will currently prevent others from receiving updates. Wrap callbacks to avoid cascading failures.

-  for (const listener of listeners) {
-    listener(state);
-  }
+  for (const listener of listeners) {
+    try {
+      listener(state);
+    } catch {
+      // Swallow to isolate subscribers; consider surfacing via a diagnostic hook if needed.
+    }
+  }
packages/react/src/hooks/use-client-query.ts (2)

107-110: Optionally refetch on client instance change.

Switching clients usually implies new config/context; consider a refetch when client changes (skip on first mount).

 useEffect(() => {
   fetchIdRef.current += 1;
-}, [client]);
+}, [client]);
+
+useEffect(() => {
+  if (!client || !enabled) return;
+  if (!hasFetchedRef.current) return; // avoid double-fetch on initial mount
+  void execute(latestArgsRef.current).catch(() => {});
+}, [client, enabled, execute]);

44-51: Stable key hashing (optional).

JSON.stringify can flap when object key order differs. If your queryKey includes objects, prefer a stable hash.

-function toHash(key: ReadonlyArray<unknown> | string): string {
+function toHash(key: ReadonlyArray<unknown> | string): string {
   if (typeof key === "string") {
     return key;
   }
-
-  return JSON.stringify(key);
+  const stable = (v: unknown): unknown =>
+    v && typeof v === "object" && !Array.isArray(v)
+      ? Object.keys(v as Record<string, unknown>)
+          .sort()
+          .reduce<Record<string, unknown>>((acc, k) => {
+            acc[k] = stable((v as any)[k]);
+            return acc;
+          }, {})
+      : Array.isArray(v)
+        ? (v as unknown[]).map(stable)
+        : v;
+  return JSON.stringify(stable(key));
 }
packages/core/src/store/conversations-store.ts (2)

161-179: Consider reordering on single-conversation updates.

ingestConversation appends new IDs but doesn’t move updated ones to the front, which can leave “recently active” threads buried. Recommend moving the conversation to the head when it’s new or changed.

Apply:

 function applyConversation(
   state: ConversationsState,
   conversation: Conversation
 ): ConversationsState {
   const previous = state.byId[conversation.id];
-  const sameConversation = previous ? isSameConversation(previous, conversation) : false;
-  const byId = sameConversation ? state.byId : { ...state.byId, [conversation.id]: conversation };
-  const hasId = state.ids.includes(conversation.id);
-  const ids = hasId ? state.ids : [...state.ids, conversation.id];
+  const changed = previous ? !isSameConversation(previous, conversation) : true;
+  const byId = changed ? { ...state.byId, [conversation.id]: conversation } : state.byId;
+  const hasId = state.ids.includes(conversation.id);
+  let ids = state.ids;
+  if (!hasId) {
+    // New: place at front
+    ids = [conversation.id, ...state.ids];
+  } else if (changed) {
+    // Updated: move to front
+    const rest = state.ids.filter((id) => id !== conversation.id);
+    ids = [conversation.id, ...rest];
+  }
 
   if (byId === state.byId && ids === state.ids) {
     return state;
   }
 
   return {
     byId,
     ids,
     pagination: state.pagination,
   };
 }

94-101: Minor perf: avoid O(n×m) includes on first-page merge.

Use a Set for incoming IDs.

-  const rest = existing.filter((id) => !incoming.includes(id));
-  const next = [...incoming, ...rest];
+  const incomingSet = new Set(incoming);
+  const rest = existing.filter((id) => !incomingSet.has(id));
+  const next = [...incoming, ...rest];
packages/react/src/hooks/use-conversations-store.ts (1)

50-59: Reduce duplication by reusing core selector.

You can rely on core’s getConversations to keep derivation logic consistent across packages.

-import { useSupport } from "../provider";
+import { useSupport } from "../provider";
+import { getConversations } from "@cossistant/core";
@@
-  return useStoreSelector(
-    client.conversationsStore,
-    (state: ConversationsState) => ({
-      conversations: state.ids
-        .map((id) => state.byId[id])
-        .filter((conversation): conversation is Conversation => conversation !== undefined),
-      pagination: state.pagination,
-    }),
-    areSelectionsEqual
-  );
+  return useStoreSelector(
+    client.conversationsStore,
+    (state: ConversationsState) => ({
+      conversations: getConversations(client.conversationsStore),
+      pagination: state.pagination,
+    }),
+    areSelectionsEqual
+  );
packages/react/src/hooks/use-conversation-messages.ts (2)

20-25: Avoid re-renders from new object snapshots in fallback store.

getState returns a fresh object each time. Return a frozen singleton to keep referential stability.

-const EMPTY_MESSAGES_STORE = {
-	getState: (): MessagesState => ({ conversations: {} }),
+const EMPTY_MESSAGES_STATE: MessagesState = Object.freeze({ conversations: {} });
+const EMPTY_MESSAGES_STORE = {
+	getState: (): MessagesState => EMPTY_MESSAGES_STATE,
 	subscribe: (_listener: (state: MessagesState) => void) => () => {
 		// noop
 	},
 };

111-113: Expose refetching state or clarify isLoading semantics.

isLoading reflects only the initial load; subsequent refetches won’t toggle it. Consider returning an isRefetching flag (pass through queryLoading) or rename to isInitialLoading to avoid confusion.

packages/core/src/store/messages-store.ts (1)

20-22: Deterministic sort with tiebreakers.

Add secondary keys to avoid flicker when timestamps collide.

-function sortMessages(messages: Message[]): Message[] {
-	return [...messages].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
-}
+function sortMessages(messages: Message[]): Message[] {
+	return [...messages].sort((a, b) => {
+		const c = a.createdAt.getTime() - b.createdAt.getTime();
+		if (c !== 0) return c;
+		const u = a.updatedAt.getTime() - b.updatedAt.getTime();
+		if (u !== 0) return u;
+		return a.id.localeCompare(b.id);
+	});
+}
packages/react/src/support/hooks/use-conversations.ts (2)

87-95: Expose isFetching to reflect background refetches; keep isLoading for initial-only.

Current isLoading becomes false after first load even while refetching, hiding in-flight states from consumers.

Apply this diff to add isFetching without breaking initial-loading semantics:

 export type UseConversationsResult = {
         conversations: ListConversationsResponse["conversations"];
         pagination: ConversationPagination | null;
         isLoading: boolean;
+        isFetching: boolean;
         error: Error | null;
         refetch: (
                 args?: Partial<ListConversationsRequest>
         ) => Promise<ListConversationsResponse | undefined>;
 };

-        const { refetch: queryRefetch, isLoading: queryLoading, error } = useClientQuery<
+        const { refetch: queryRefetch, isLoading: queryIsLoading, error } = useClientQuery<
                 ListConversationsResponse,
                 Partial<ListConversationsRequest>
         >({
                 client,
                 queryKey: [
                         "conversations",
                         clientKey,
                         requestDefaults.limit ?? null,
                         requestDefaults.page ?? null,
                         requestDefaults.status ?? null,
                         requestDefaults.orderBy ?? null,
                         requestDefaults.order ?? null,
                 ],
                 queryFn: (instance, args) =>
                         instance.listConversations({
                                 ...requestDefaults,
                                 ...args,
                         }),
                 enabled: Boolean(client && enabled),
                 refetchInterval,
                 refetchOnWindowFocus,
                 refetchOnMount: selection.conversations.length === 0,
                 initialArgs: requestDefaults,
         });

         const isInitialLoad = selection.conversations.length === 0;
-        const isLoading = isInitialLoad ? queryLoading : false;
+        const isLoading = isInitialLoad ? queryIsLoading : false;

         return {
                 conversations: selection.conversations,
                 pagination: selection.pagination,
                 isLoading,
+                isFetching: queryIsLoading,
                 error,
                 refetch,
         };

Also applies to: 148-172, 182-191


97-105: Add concise JSDoc for the public hook and overloads.

Documents purpose, options, provider dependency, and return shape for consumers.

packages/core/src/client.ts (3)

308-316: Update conversation.lastMessage after successful send.

Non‑pending path finalizes the message but leaves conversationsStore.lastMessage stale, so lists won’t reflect the latest message.

Apply this diff:

                 try {
                         const response = await this.restClient.sendMessage(payload);
                         this.messagesStore.finalizeMessage(rest.conversationId, optimisticId, response.message);
+                        const existing = this.conversationsStore.getState().byId[rest.conversationId];
+                        if (existing) {
+                                this.conversationsStore.ingestConversation({
+                                        ...existing,
+                                        lastMessage: response.message,
+                                        updatedAt: response.message.createdAt,
+                                });
+                        }
                         return response;
                 } catch (error) {

319-327: Propagate realtime MESSAGE_CREATED to conversationsStore (lastMessage/updatedAt).

Currently only messagesStore is updated; conversation lists can get stale.

Apply this diff:

         handleRealtimeEvent(event: RealtimeEvent): void {
                 switch (event.type) {
                         case "MESSAGE_CREATED":
                                 this.messagesStore.ingestRealtime(event);
+                                {
+                                        const msg = (event as any).data?.message as Message | undefined;
+                                        if (msg) {
+                                                const existing = this.conversationsStore.getState().byId[msg.conversationId];
+                                                if (existing) {
+                                                        this.conversationsStore.ingestConversation({
+                                                                ...existing,
+                                                                lastMessage: msg,
+                                                                updatedAt: msg.createdAt,
+                                                        });
+                                                }
+                                        }
+                                }
                                 break;
                         default:
                                 break;
                 }
         }

127-129: Consider caching in getWebsite() instead of forcing network.

getWebsite always forces a fetch; many callers expect a cached read with internal refresh via fetchWebsite. Optional, but reduces redundant calls.

-        async getWebsite(): Promise<PublicWebsiteResponse> {
-                return this.fetchWebsite({ force: true });
-        }
+        async getWebsite(): Promise<PublicWebsiteResponse> {
+                return this.fetchWebsite();
+        }

Also applies to: 90-126

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 475cda9 and 54b5be5.

📒 Files selected for processing (26)
  • packages/core/LLM_GUIDE.md (1 hunks)
  • packages/core/src/client.ts (4 hunks)
  • packages/core/src/index.ts (1 hunks)
  • packages/core/src/rest-client.ts (1 hunks)
  • packages/core/src/store/conversations-store.test.ts (1 hunks)
  • packages/core/src/store/conversations-store.ts (1 hunks)
  • packages/core/src/store/create-store.ts (1 hunks)
  • packages/core/src/store/messages-store.test.ts (1 hunks)
  • packages/core/src/store/messages-store.ts (1 hunks)
  • packages/core/src/store/website-store.test.ts (1 hunks)
  • packages/core/src/store/website-store.ts (1 hunks)
  • packages/react/src/hooks/index.ts (1 hunks)
  • packages/react/src/hooks/use-client-query.ts (1 hunks)
  • packages/react/src/hooks/use-conversation-messages.ts (1 hunks)
  • packages/react/src/hooks/use-conversations-store.ts (1 hunks)
  • packages/react/src/hooks/use-store-selector.ts (1 hunks)
  • packages/react/src/hooks/use-website-store.ts (1 hunks)
  • packages/react/src/index.ts (1 hunks)
  • packages/react/src/provider.tsx (2 hunks)
  • packages/react/src/support/hooks/index.ts (1 hunks)
  • packages/react/src/support/hooks/use-conversation.ts (1 hunks)
  • packages/react/src/support/hooks/use-conversations.ts (1 hunks)
  • packages/react/src/support/hooks/use-website-data.ts (0 hunks)
  • packages/react/src/support/hooks/use-website.ts (1 hunks)
  • packages/react/src/support/pages/conversation-history.tsx (1 hunks)
  • packages/react/src/support/pages/home.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/src/support/hooks/use-website-data.ts
🧰 Additional context used
📓 Path-based instructions (15)
packages/react/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Develop headless React components (including and primitives) under packages/react/src/

Files:

  • packages/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/react/src/index.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/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/core/src/rest-client.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/core/src/store/website-store.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/index.ts
  • packages/core/src/client.ts
  • packages/react/src/index.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/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/core/src/rest-client.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/core/src/store/website-store.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/index.ts
  • packages/core/src/client.ts
  • packages/react/src/index.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/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/core/src/rest-client.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/core/src/store/website-store.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/hooks/use-website-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/index.ts
  • packages/core/src/client.ts
  • packages/react/src/index.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/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/core/src/rest-client.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/core/src/store/website-store.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/index.ts
  • packages/core/src/client.ts
  • packages/react/src/index.ts
**/*.{js,tsx,ts,jsx}

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

Use arrow functions instead of function expressions

Files:

  • packages/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/core/src/rest-client.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/core/src/store/website-store.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/index.ts
  • packages/core/src/client.ts
  • packages/react/src/index.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/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/core/src/rest-client.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/core/src/store/website-store.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/index.ts
  • packages/core/src/client.ts
  • packages/react/src/index.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/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/core/src/rest-client.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/core/src/store/website-store.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/index.ts
  • packages/core/src/client.ts
  • packages/react/src/index.ts
packages/react/**/*.{ts,tsx}

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

packages/react/**/*.{ts,tsx}: Components must render with no built-in styles (headless, unstyled)
Components must accept className, style, and asChild props to enable external styling/control
Do not use emotion, styled-components, or style-prop APIs; styling is via Tailwind/CSS through className/style
Use useRenderElement to create elements and encapsulate render logic (supporting intrinsic tags and render prop overrides)
Components with internal state must be controllable via props (e.g., value/defaultValue/onChange or isOpen/onOpenChange)
Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child
May reference Base UI logic internally, but never expose Base UI or MUI APIs; do not leak getRootProps/getInputProps; prefer useRenderElement
Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components
Provide a render prop API: render?: (props, state) => ReactElement, and allow className to be string or (state) => string
Support developer-owned rendering: components should not impose visual structure and should allow parent-driven markup via render prop/asChild

Files:

  • packages/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/react/src/index.ts
packages/**

📄 CodeRabbit inference engine (AGENTS.md)

Put shared contracts and utilities in packages/* (e.g., core, types, transactional)

Files:

  • packages/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/index.ts
  • packages/core/src/rest-client.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/pages/home.tsx
  • packages/core/LLM_GUIDE.md
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/index.ts
  • packages/core/src/store/website-store.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/support/pages/conversation-history.tsx
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/index.ts
  • packages/core/src/client.ts
  • packages/react/src/index.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:

  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.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:

  • packages/core/src/store/messages-store.test.ts
  • packages/core/src/store/website-store.test.ts
  • packages/core/src/store/conversations-store.test.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:

  • packages/react/src/support/pages/home.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/support/pages/conversation-history.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:

  • packages/react/src/support/pages/home.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/support/pages/conversation-history.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:

  • packages/react/src/support/pages/home.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/support/pages/conversation-history.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to apps/web/src/hooks/use*.{ts,tsx} : Name React hooks with a use* prefix and place them in apps/web/src/hooks

Applied to files:

  • packages/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-website-store.ts
📚 Learning: 2025-09-17T16:59:53.676Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/react-package-rules.mdc:0-0
Timestamp: 2025-09-17T16:59:53.676Z
Learning: Applies to packages/react/**/*.{ts,tsx} : Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child

Applied to files:

  • packages/react/src/hooks/index.ts
  • packages/react/src/support/hooks/index.ts
🧬 Code graph analysis (19)
packages/react/src/support/hooks/use-website.ts (2)
packages/react/src/hooks/use-website-store.ts (3)
  • UseWebsiteStoreOptions (46-49)
  • UseWebsiteStoreResult (38-44)
  • useWebsiteStore (63-103)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-conversations-store.ts (4)
packages/core/src/index.ts (2)
  • ConversationPagination (16-16)
  • ConversationsState (17-17)
packages/core/src/store/conversations-store.ts (2)
  • ConversationPagination (5-5)
  • ConversationsState (7-11)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-30)
packages/react/src/hooks/use-client-query.ts (1)
packages/core/src/client.ts (1)
  • CossistantClient (62-333)
packages/core/src/rest-client.ts (1)
packages/core/src/visitor-tracker.ts (2)
  • setVisitorId (64-83)
  • getVisitorId (36-59)
packages/core/src/store/conversations-store.ts (2)
packages/types/src/api/conversation.ts (1)
  • ListConversationsResponse (109-111)
packages/core/src/store/create-store.ts (2)
  • Store (7-12)
  • createStore (14-68)
packages/core/src/store/messages-store.test.ts (5)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (60-64)
packages/core/src/store/messages-store.ts (1)
  • createMessagesStore (188-210)
packages/core/src/client.ts (1)
  • CossistantClient (62-333)
packages/types/src/api/message.ts (1)
  • SendMessageRequest (37-37)
packages/types/src/api/conversation.ts (1)
  • CreateConversationRequestBody (35-37)
packages/core/src/store/messages-store.ts (4)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (60-64)
packages/core/src/store/create-store.ts (2)
  • Store (7-12)
  • createStore (14-68)
packages/core/src/client.ts (1)
  • getConversationMessages (220-230)
packages/core/src/rest-client.ts (1)
  • getConversationMessages (514-574)
packages/react/src/hooks/use-store-selector.ts (1)
packages/core/src/store/create-store.ts (1)
  • subscribe (49-54)
packages/react/src/hooks/use-conversation-messages.ts (4)
packages/core/src/store/messages-store.ts (2)
  • ConversationMessagesState (6-10)
  • MessagesState (12-14)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-30)
packages/react/src/hooks/use-client-query.ts (1)
  • useClientQuery (52-249)
packages/react/src/support/pages/home.tsx (2)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/support/hooks/use-conversations.ts (1)
  • useConversations (102-192)
packages/react/src/support/hooks/use-conversation.ts (5)
packages/core/src/store/conversations-store.ts (1)
  • ConversationsState (7-11)
packages/types/src/api/conversation.ts (2)
  • GetConversationResponse (135-137)
  • GetConversationRequest (123-125)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-30)
packages/react/src/hooks/use-client-query.ts (1)
  • useClientQuery (52-249)
packages/core/src/store/website-store.ts (1)
packages/core/src/store/create-store.ts (2)
  • Store (7-12)
  • createStore (14-68)
packages/react/src/support/hooks/use-conversations.ts (6)
packages/types/src/api/conversation.ts (2)
  • ListConversationsResponse (109-111)
  • ListConversationsRequest (90-92)
packages/core/src/store/conversations-store.ts (2)
  • ConversationPagination (5-5)
  • ConversationsState (7-11)
packages/core/src/client.ts (1)
  • CossistantClient (62-333)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-30)
packages/react/src/hooks/use-client-query.ts (1)
  • useClientQuery (52-249)
packages/react/src/provider.tsx (2)
packages/react/src/hooks/use-rest-client.ts (1)
  • useClient (18-69)
packages/react/src/hooks/use-website-store.ts (1)
  • useWebsiteStore (63-103)
packages/react/src/hooks/use-website-store.ts (4)
packages/core/src/store/website-store.ts (2)
  • WebsiteState (10-14)
  • WebsiteStore (34-39)
packages/core/src/client.ts (1)
  • CossistantClient (62-333)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-30)
packages/react/src/hooks/use-client-query.ts (1)
  • useClientQuery (52-249)
packages/react/src/support/pages/conversation-history.tsx (2)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/support/hooks/use-conversations.ts (1)
  • useConversations (102-192)
packages/core/src/store/website-store.test.ts (2)
packages/core/src/store/website-store.ts (2)
  • createWebsiteStore (41-91)
  • WebsiteState (10-14)
packages/core/src/client.ts (1)
  • CossistantClient (62-333)
packages/core/src/store/conversations-store.test.ts (3)
packages/types/src/api/conversation.ts (1)
  • ListConversationsResponse (109-111)
packages/core/src/store/conversations-store.ts (3)
  • createConversationsStore (187-201)
  • getConversations (203-210)
  • getConversationById (212-217)
packages/core/src/client.ts (1)
  • CossistantClient (62-333)
packages/core/src/client.ts (8)
packages/types/src/schemas.ts (2)
  • Conversation (58-58)
  • Message (25-25)
packages/core/src/rest-client.ts (2)
  • CossistantRestClient (43-623)
  • request (149-173)
packages/core/src/store/conversations-store.ts (2)
  • ConversationsStore (182-185)
  • createConversationsStore (187-201)
packages/core/src/store/messages-store.ts (2)
  • MessagesStore (180-186)
  • createMessagesStore (188-210)
packages/core/src/store/website-store.ts (3)
  • WebsiteStore (34-39)
  • createWebsiteStore (41-91)
  • WebsiteState (10-14)
packages/core/src/utils.ts (2)
  • generateConversationId (13-16)
  • generateMessageId (18-20)
packages/types/src/enums.ts (6)
  • ConversationStatus (17-21)
  • ConversationStatus (23-24)
  • MessageType (1-5)
  • MessageType (7-7)
  • MessageVisibility (33-36)
  • MessageVisibility (74-75)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (60-64)
🪛 markdownlint-cli2 (0.18.1)
packages/core/LLM_GUIDE.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread packages/core/src/client.ts Outdated
Comment on lines +263 to +297
const pending = this.pendingConversations.get(rest.conversationId);

if (pending && createIfPending !== false) {
try {
const response = await this.restClient.createConversation({
conversationId: rest.conversationId,
defaultMessages: [...pending.defaultMessages, optimisticMessage],
});

this.conversationsStore.ingestConversation(response.conversation);
this.messagesStore.removeMessage(rest.conversationId, optimisticId);
this.messagesStore.ingestPage(rest.conversationId, {
messages: response.initialMessages,
hasNextPage: false,
nextCursor: undefined,
});

this.pendingConversations.delete(rest.conversationId);

return {
message: response.initialMessages[response.initialMessages.length - 1] ??
response.initialMessages[0],
conversation: response.conversation,
initialMessages: response.initialMessages,
wasConversationCreated: true,
} satisfies SendMessageResponse & {
conversation: Conversation;
initialMessages: Message[];
wasConversationCreated: true;
};
} catch (error) {
this.messagesStore.removeMessage(rest.conversationId, optimisticId);
throw error;
}
}

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 | 🟠 Major

Deduplicate concurrent pending-conversation creations to avoid double POSTs and message loss.

Two rapid sends on a pending conversation can race, causing a second createConversation call to fail and the optimistic message to be dropped.

Apply this diff to gate by an in‑flight map:

@@
 export class CossistantClient {
@@
-        private pendingConversations = new Map<string, PendingConversation>();
+        private pendingConversations = new Map<string, PendingConversation>();
+        private creatingConversations = new Map<string, Promise<CreateConversationResponseBody>>();
@@
-                if (pending && createIfPending !== false) {
+                if (pending && createIfPending !== false) {
                         try {
-                                const response = await this.restClient.createConversation({
-                                        conversationId: rest.conversationId,
-                                        defaultMessages: [...pending.defaultMessages, optimisticMessage],
-                                });
+                                let request = this.creatingConversations.get(rest.conversationId);
+                                if (!request) {
+                                        request = this.restClient.createConversation({
+                                                conversationId: rest.conversationId,
+                                                defaultMessages: [...pending.defaultMessages, optimisticMessage],
+                                        });
+                                        this.creatingConversations.set(rest.conversationId, request);
+                                }
+                                const response = await request.finally(() => {
+                                        this.creatingConversations.delete(rest.conversationId);
+                                });
📝 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
const pending = this.pendingConversations.get(rest.conversationId);
if (pending && createIfPending !== false) {
try {
const response = await this.restClient.createConversation({
conversationId: rest.conversationId,
defaultMessages: [...pending.defaultMessages, optimisticMessage],
});
this.conversationsStore.ingestConversation(response.conversation);
this.messagesStore.removeMessage(rest.conversationId, optimisticId);
this.messagesStore.ingestPage(rest.conversationId, {
messages: response.initialMessages,
hasNextPage: false,
nextCursor: undefined,
});
this.pendingConversations.delete(rest.conversationId);
return {
message: response.initialMessages[response.initialMessages.length - 1] ??
response.initialMessages[0],
conversation: response.conversation,
initialMessages: response.initialMessages,
wasConversationCreated: true,
} satisfies SendMessageResponse & {
conversation: Conversation;
initialMessages: Message[];
wasConversationCreated: true;
};
} catch (error) {
this.messagesStore.removeMessage(rest.conversationId, optimisticId);
throw error;
}
}
// In packages/core/src/client.ts
export class CossistantClient {
private pendingConversations = new Map<string, PendingConversation>();
private creatingConversations = new Map<string, Promise<CreateConversationResponseBody>>();
// … other members …
private async sendMessage(rest: SendMessageRequest, createIfPending = true): Promise<SendMessageResponse> {
const pending = this.pendingConversations.get(rest.conversationId);
if (pending && createIfPending !== false) {
try {
let request = this.creatingConversations.get(rest.conversationId);
if (!request) {
request = this.restClient.createConversation({
conversationId: rest.conversationId,
defaultMessages: [...pending.defaultMessages, optimisticMessage],
});
this.creatingConversations.set(rest.conversationId, request);
}
const response = await request.finally(() => {
this.creatingConversations.delete(rest.conversationId);
});
this.conversationsStore.ingestConversation(response.conversation);
this.messagesStore.removeMessage(rest.conversationId, optimisticId);
this.messagesStore.ingestPage(rest.conversationId, {
messages: response.initialMessages,
hasNextPage: false,
nextCursor: undefined,
});
this.pendingConversations.delete(rest.conversationId);
return {
message: response.initialMessages[response.initialMessages.length - 1] ??
response.initialMessages[0],
conversation: response.conversation,
initialMessages: response.initialMessages,
wasConversationCreated: true,
} satisfies SendMessageResponse & {
conversation: Conversation;
initialMessages: Message[];
wasConversationCreated: true;
};
} catch (error) {
this.messagesStore.removeMessage(rest.conversationId, optimisticId);
throw error;
}
}
// … fallback for non-pending or createIfPending === false …
}
}
🤖 Prompt for AI Agents
packages/core/src/client.ts around lines 263-297: concurrent sends for the same
pending conversation can trigger duplicate createConversation POSTs and drop
optimistic messages; add an in-flight map keyed by conversationId to deduplicate
and await an existing create promise instead of issuing a second POST. On entry,
check the inFlightCreates map for rest.conversationId and if present await that
promise and then return the same response handling (ingest conversation/messages
and remove optimistic message as done currently); if not present, create a
Promise for the createConversation call, store it in the map before calling
restClient.createConversation, and ensure you remove the map entry in both
success and catch paths so subsequent attempts can proceed; preserve existing
messageStore and conversationsStore updates and rethrow errors after cleaning
up.

Comment on lines +24 to +31
function isSameMessage(a: Message, b: Message): boolean {
return (
a.id === b.id &&
a.bodyMd === b.bodyMd &&
a.updatedAt.getTime() === b.updatedAt.getTime() &&
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 | 🟠 Major

Broaden equality check to catch visibility/deletion changes.

isSameMessage ignores fields like visibility and deletedAt. If the backend doesn’t bump updatedAt for those, changes will be missed.

 function isSameMessage(a: Message, b: Message): boolean {
 	return (
 		a.id === b.id &&
 		a.bodyMd === b.bodyMd &&
 		a.updatedAt.getTime() === b.updatedAt.getTime() &&
-		a.createdAt.getTime() === b.createdAt.getTime()
+		a.createdAt.getTime() === b.createdAt.getTime() &&
+		a.visibility === b.visibility &&
+		(a.deletedAt?.getTime() ?? -1) === (b.deletedAt?.getTime() ?? -1)
 	);
 }
🤖 Prompt for AI Agents
In packages/core/src/store/messages-store.ts around lines 24 to 31,
isSameMessage currently compares id, bodyMd, createdAt and updatedAt but omits
visibility and deletedAt so changes to those fields will be missed; update the
equality check to also compare message.visibility (exact equality) and
message.deletedAt (treat null/undefined consistently and compare .getTime() for
Date values), ensuring you handle cases where deletedAt can be undefined/null
before calling getTime, and update any relevant tests or callers accordingly.

Comment on lines +33 to +67
function mergeMessages(existing: Message[], incoming: Message[]): Message[] {
if (incoming.length === 0) {
return existing;
}

const byId = new Map<string, Message>();
for (const message of existing) {
byId.set(message.id, message);
}

let changed = false;
for (const message of incoming) {
const previous = byId.get(message.id);
if (!previous || !isSameMessage(previous, message)) {
changed = true;
}
byId.set(message.id, message);
}

if (!changed && byId.size === existing.length) {
let orderStable = true;
for (const message of existing) {
if (byId.get(message.id) !== message) {
orderStable = false;
break;
}
}

if (orderStable) {
return existing;
}
}

return sortMessages(Array.from(byId.values()));
}

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 | 🟠 Major

Preserve object identity for unchanged messages to prevent needless updates.

Even when content is equal, you replace map entries with the incoming object, breaking referential checks and causing avoidable re-renders/store writes.

 for (const message of incoming) {
 	const previous = byId.get(message.id);
-	if (!previous || !isSameMessage(previous, message)) {
-		changed = true;
-	}
-	byId.set(message.id, message);
+	if (!previous) {
+		changed = true;
+		byId.set(message.id, message);
+	} else if (!isSameMessage(previous, message)) {
+		changed = true;
+		byId.set(message.id, message);
+	} else {
+		// Keep previous reference to maintain structural sharing.
+		byId.set(message.id, previous);
+	}
 }
📝 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
function mergeMessages(existing: Message[], incoming: Message[]): Message[] {
if (incoming.length === 0) {
return existing;
}
const byId = new Map<string, Message>();
for (const message of existing) {
byId.set(message.id, message);
}
let changed = false;
for (const message of incoming) {
const previous = byId.get(message.id);
if (!previous || !isSameMessage(previous, message)) {
changed = true;
}
byId.set(message.id, message);
}
if (!changed && byId.size === existing.length) {
let orderStable = true;
for (const message of existing) {
if (byId.get(message.id) !== message) {
orderStable = false;
break;
}
}
if (orderStable) {
return existing;
}
}
return sortMessages(Array.from(byId.values()));
}
function mergeMessages(existing: Message[], incoming: Message[]): Message[] {
if (incoming.length === 0) {
return existing;
}
const byId = new Map<string, Message>();
for (const message of existing) {
byId.set(message.id, message);
}
let changed = false;
for (const message of incoming) {
const previous = byId.get(message.id);
if (!previous) {
changed = true;
byId.set(message.id, message);
} else if (!isSameMessage(previous, message)) {
changed = true;
byId.set(message.id, message);
} else {
// Keep previous reference to maintain structural sharing.
byId.set(message.id, previous);
}
}
if (!changed && byId.size === existing.length) {
let orderStable = true;
for (const message of existing) {
if (byId.get(message.id) !== message) {
orderStable = false;
break;
}
}
if (orderStable) {
return existing;
}
}
return sortMessages(Array.from(byId.values()));
}
🤖 Prompt for AI Agents
In packages/core/src/store/messages-store.ts around lines 33 to 67, the
mergeMessages routine currently replaces existing Message objects with incoming
ones even when isSameMessage(previous, message) returns true, breaking object
identity and causing unnecessary updates; change the loop that iterates incoming
messages so that if previous exists and isSameMessage(previous, message) is true
you keep the previous object in the map (byId.set(message.id, previous)) and
only replace/set with the incoming message when there is no previous or messages
differ, preserving referential equality and leaving the existing array instance
early when no changes are detected.

Comment on lines +87 to +91
reset() {
store.setState(() => INITIAL_STATE);
},
};
}

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 | 🟠 Major

Reset should restore to the caller’s initial state, not the module constant.

If a custom initialState is passed to createWebsiteStore, reset() currently reverts to INITIAL_STATE, not the provided baseline.

Apply this diff to snapshot the caller’s initial state and use it for reset:

 export function createWebsiteStore(initialState: WebsiteState = INITIAL_STATE): WebsiteStore {
-        const store = createStore<WebsiteState>(initialState);
+        const store = createStore<WebsiteState>(initialState);
+        const initialSnapshot: WebsiteState = { ...initialState };

         return {
                 ...store,
                 setLoading() {
@@
                 },
                 reset() {
-                        store.setState(() => INITIAL_STATE);
+                        store.setState(() => initialSnapshot);
                 },
         };
 }
🤖 Prompt for AI Agents
In packages/core/src/store/website-store.ts around lines 87-91, reset()
currently sets state to the module-level INITIAL_STATE instead of the
caller-provided initialState; capture the provided initialState when
createWebsiteStore is called (e.g., const baseline = initialState ??
INITIAL_STATE and deep/shallow clone it to avoid external mutation) and change
reset() to restore that baseline clone via store.setState(() =>
cloneOfBaseline). Ensure baseline is computed once at creation and used
immutably for subsequent resets.

Comment on lines +173 to +176
keyChangedRef.current = false;
hasFetchedRef.current = true;
void execute(latestArgsRef.current);
}, [client, enabled, execute, keyHash, refetchOnMount]);

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 | 🟠 Major

Avoid unhandled promise rejections from internal fetches.

execute() rethrows; internal calls aren’t awaited, which can surface unhandled rejections. Catch them where invoked.

Apply these changes:

-    void execute(latestArgsRef.current);
+    void execute(latestArgsRef.current).catch(() => {});
-    const timer = window.setInterval(() => {
-      void execute(latestArgsRef.current);
-    }, refetchInterval);
+    const timer = window.setInterval(() => {
+      void execute(latestArgsRef.current).catch(() => {});
+    }, refetchInterval);
-    const handleRefetch = () => {
+    const handleRefetch = () => {
       if (!client || !enabled) {
         return;
       }
-
-      void execute(latestArgsRef.current);
+      void execute(latestArgsRef.current).catch(() => {});
     };

Also applies to: 191-197, 209-216

🤖 Prompt for AI Agents
In packages/react/src/hooks/use-client-query.ts around lines 173-176 (and
likewise for the other internal calls at 191-197 and 209-216), internal calls to
execute(...) are invoked without handling rejections which can cause unhandled
promise rejections; update those invocations to catch and handle any rejection
from execute — e.g. call execute(latestArgsRef.current).catch(err => {/* handle
or log error */}) or use void execute(...).catch(err => {/* handle or log error
*/}) so every internal fire-and-forget invocation has a .catch that at minimum
logs the error.

Comment on lines +20 to +25
const EMPTY_MESSAGES_STORE = {
getState: (): MessagesState => ({ conversations: {} }),
subscribe: () => () => {
// noop
},
};

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 | 🔴 Critical

Fix subscribe signature on EMPTY_MESSAGES_STORE (type/runtime bug).

useStoreSelector expects subscribe(listener) => unsubscribe. This fallback takes no listener, which will fail type-checking and suppress updates.

Apply:

-const EMPTY_MESSAGES_STORE = {
-	getState: (): MessagesState => ({ conversations: {} }),
-	subscribe: () => () => {
-		// noop
-	},
-};
+const EMPTY_MESSAGES_STORE = {
+	getState: (): MessagesState => ({ conversations: {} }),
+	subscribe: (_listener: (state: MessagesState) => void) => () => {
+		// noop
+	},
+};
📝 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
const EMPTY_MESSAGES_STORE = {
getState: (): MessagesState => ({ conversations: {} }),
subscribe: () => () => {
// noop
},
};
const EMPTY_MESSAGES_STORE = {
getState: (): MessagesState => ({ conversations: {} }),
subscribe: (_listener: (state: MessagesState) => void) => () => {
// noop
},
};
🤖 Prompt for AI Agents
In packages/react/src/hooks/use-conversation-messages.ts around lines 20 to 25,
the EMPTY_MESSAGES_STORE.subscribe currently takes no arguments which breaks
useStoreSelector's expected subscribe(listener) => unsubscribe signature; change
subscribe to accept a listener parameter (e.g. subscribe: (listener: () => void)
=> () => void) and return an unsubscribe function that is a noop, ensuring the
type matches the store subscriber type so type-checking and runtime updates work
correctly.

Comment on lines +70 to +79
const query = useClientQuery<PublicWebsiteResponse, { force?: boolean }>({
client,
queryKey: ["website", client],
queryFn: (instance, params) => instance.fetchWebsite(params ?? {}),
enabled: Boolean(client),
refetchInterval: options.refetchInterval ?? false,
refetchOnWindowFocus: options.refetchOnWindowFocus ?? true,
refetchOnMount: state.status === "idle",
initialData: state.website ?? undefined,
});

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 | 🔴 Critical

🧩 Analysis chain

queryKey includes a non-serializable object; verify hashing or switch to a stable scalar key

If useClientQuery hashes keys via JSON.stringify (or similar), including the client object can throw (circular) or collapse to “[object Object]”, breaking cache segregation across client/context. Prefer a stable scalar key (e.g., ["website", clientContextId]) or enhance hashing to handle object identity.

Please confirm how keys are hashed:

If serialization is used, consider:

  • Expose a public accessor on CossistantClient to return a stable context key (e.g., websiteId:visitorId), then:
    queryKey: ["website", client.getContextKey()]
  • Or change useClientQuery to support object identity keys (WeakMap-based) rather than stringification.

I can draft a small utility for stable key derivation once we pick an approach.


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -nP 'function\s+useClientQuery|hash|queryKey' packages/react/src/hooks/use-client-query.ts -n -C3

Length of output: 737


Replace client object in queryKey with a stable scalar
useClientQuery’s toHash calls JSON.stringify(key), so including the CossistantClient instance will either throw on circular refs or serialize to {}, collapsing all client keys and breaking cache isolation. Switch to a stable scalar identifier instead of the object, for example:

queryKey: ["website", client.getContextKey()]
🤖 Prompt for AI Agents
In packages/react/src/hooks/use-website-store.ts around lines 70 to 79, the
queryKey currently includes the CossistantClient object which causes
JSON.stringify() to fail or collapse keys; replace the object with a stable
scalar identifier (e.g. call a client.getContextKey() or client.id property) so
the key becomes ["website", clientContextKey]; ensure the chosen scalar is
stable across renders and uniquely identifies the client context and update any
type usages accordingly.

Comment thread packages/react/src/index.ts
Comment on lines +13 to +18
const EMPTY_STORE = {
getState: (): ConversationsState => EMPTY_STATE,
subscribe: () => () => {
// noop
},
};

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 | 🔴 Critical

Type mismatch in EMPTY_STORE.subscribe.

subscribe should accept a listener argument; current signature may fail TS checks when unioned with real store.

Apply:

-const EMPTY_STORE = {
-        getState: (): ConversationsState => EMPTY_STATE,
-        subscribe: () => () => {
-                // noop
-        },
-};
+const EMPTY_STORE = {
+        getState: (): ConversationsState => EMPTY_STATE,
+        subscribe: (_listener: (state: ConversationsState) => void) => () => {
+                // noop
+        },
+};
📝 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
const EMPTY_STORE = {
getState: (): ConversationsState => EMPTY_STATE,
subscribe: () => () => {
// noop
},
};
const EMPTY_STORE = {
getState: (): ConversationsState => EMPTY_STATE,
subscribe: (_listener: (state: ConversationsState) => void) => () => {
// noop
},
};
🤖 Prompt for AI Agents
In packages/react/src/support/hooks/use-conversation.ts around lines 13 to 18,
EMPTY_STORE.subscribe currently has no listener parameter which causes a
TypeScript mismatch when this stub is unioned with the real store; change
subscribe to accept a listener argument typed as () => void and return an
unsubscribe function typed as () => void (i.e., subscribe: (listener: () =>
void) => () => void { return () => {} }), ensuring the signature matches the
real store's subscribe and satisfies TS checks.

Comment on lines +140 to +147
const clientKey = useMemo(() => {
if (!client) {
return "no-client";
}
const configuration = client.getConfiguration();
return `${configuration.publicKey ?? "anon"}|${configuration.apiUrl ?? ""}`;
}, [client]);

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 | 🟠 Major

Fix stale query key when client config changes.

clientKey is memoized only on the client instance, so updates via client.updateConfiguration(...) won’t change the cache key, leading to stale queries/refetch gaps.

Apply this diff to compute clientKey per render:

-        const clientKey = useMemo(() => {
-                if (!client) {
-                        return "no-client";
-                }
-                const configuration = client.getConfiguration();
-                return `${configuration.publicKey ?? "anon"}|${configuration.apiUrl ?? ""}`;
-        }, [client]);
+        const configuration = client?.getConfiguration();
+        const clientKey = configuration
+                ? `${configuration.publicKey ?? "anon"}|${configuration.apiUrl ?? ""}`
+                : "no-client";
📝 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
const clientKey = useMemo(() => {
if (!client) {
return "no-client";
}
const configuration = client.getConfiguration();
return `${configuration.publicKey ?? "anon"}|${configuration.apiUrl ?? ""}`;
}, [client]);
const configuration = client?.getConfiguration();
const clientKey = configuration
? `${configuration.publicKey ?? "anon"}|${configuration.apiUrl ?? ""}`
: "no-client";
🤖 Prompt for AI Agents
In packages/react/src/support/hooks/use-conversations.ts around lines 140 to
147, clientKey is memoized only on the client instance so updates to
client.updateConfiguration(...) won’t change the key; compute clientKey on every
render instead of using useMemo so configuration changes are reflected
immediately: remove the useMemo wrapper and compute the same logic synchronously
each render (return "no-client" when client is falsy, otherwise read
client.getConfiguration() and build the `${publicKey ?? "anon"}|${apiUrl ?? ""}`
string).

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: 7

Caution

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

⚠️ Outside diff range comments (3)
packages/react/src/support/pages/conversation.tsx (1)

101-141: Avoid sending null conversationId to the API

Passing null can break serializers or violate API types. Prefer undefined when creating a new conversation.

Apply:

-		sendMessage.mutate({
-			conversationId: realConversationId,
+		sendMessage.mutate({
+			conversationId: hasRealConversation ? realConversationId! : undefined,
packages/types/src/realtime-events.ts (1)

2-2: Remove unused imports.

MessageType and MessageVisibility aren’t used.

-import { MessageType, MessageVisibility } from "./enums";
packages/react/src/support/pages/home.tsx (1)

44-61: Remove console logs and avoid side effects inside useMemo.

console.log is disallowed and useMemo should be pure. Drop logs or move them behind a dev-only useEffect.

Apply this diff:

-	console.log(
-		"[HomePage] Conversations:",
-		conversations?.map((c) => ({
-			id: c.id,
-			status: c.status,
-			updatedAt: c.updatedAt,
-		}))
-	);
-
 	const _lastOpenConversation = conversations?.find(
 		(conversation) => conversation.status === "open"
 	);
-
-	console.log(
-		"[HomePage] Last open conversation:",
-		_lastOpenConversation?.id
-	);

As per coding guidelines.

🧹 Nitpick comments (19)
apps/web/src/components/conversation/index.tsx (3)

41-53: Drop unused fields from useMultimodalInput destructuring

clearFiles, reset, isValid, canSubmit (and after the change below, addFiles/removeFile) are unused; they’ll trip linters and add noise.

Apply:

-  const {
-    message,
-    files,
-    isSubmitting,
-    error,
-    setMessage,
-    addFiles,
-    removeFile,
-    clearFiles,
-    submit,
-    reset,
-    isValid,
-    canSubmit,
-  } = useMultimodalInput({
+  const {
+    message,
+    files,
+    isSubmitting,
+    error,
+    setMessage,
+    submit,
+  } = useMultimodalInput({

133-146: Memoize onFetchMoreIfNeeded to avoid prop churn on MessagesList

Tiny perf/GC win when passed down every render.

Apply:

-  const onFetchMoreIfNeeded = async () => {
+  const onFetchMoreIfNeeded = useCallback(async () => {
     const promises: Promise<unknown>[] = [];
@@
-    await Promise.all(promises);
-  };
+    await Promise.all(promises);
+  }, [
+    hasNextPageMessages,
+    fetchNextPageMessages,
+    hasNextPageEvents,
+    fetchNextPageEvents,
+  ]);

Also add the missing import:

-import { useEffect, useMemo, useRef } from "react";
+import { useCallback, useEffect, useMemo, useRef } from "react";

89-131: Mark-as-read effect: add a simple in-flight guard

Rapid message bursts can trigger overlapping markRead calls. Guarding avoids redundant mutations.

Minimal pattern:

const markReadInFlight = useRef(false);

useEffect(() => {
  // ...existing checks above
  if (markReadInFlight.current) return;
  markReadInFlight.current = true;
  markRead()
    .then(() => {
      lastMarkedMessageIdRef.current = lastMessage.id;
    })
    .finally(() => {
      markReadInFlight.current = false;
    });
}, [/* existing deps */]);

Alternatively, if useConversationActions exposes pendingAction/isAnyPending (per its docs), gate on that instead. Please confirm availability.

biome.jsonc (1)

32-35: Don’t disable rules that your guidelines require

You turned off complexity.noVoid, but your guidelines explicitly say “Don’t use void operators.” Keep this rule enabled; consider warning on cognitive complexity instead of disabling.

-      "complexity": {
-        "noVoid": "off",
-        "noExcessiveCognitiveComplexity": "off"
-      },
+      "complexity": {
+        "noVoid": "error",
+        "noExcessiveCognitiveComplexity": "warn"
+      },

As per coding guidelines.

packages/react/src/support/pages/conversation.tsx (1)

185-185: Replace console.error with structured logging

Direct console usage is disallowed by your rules; route through a logger or dev‑only debug utility.

-				console.error("Failed to mark conversation as seen:", markSeenError);
+				// TODO: replace with structured logger (e.g., supportClient.logger.error)
+				if (process.env.NODE_ENV !== "production") {
+					// eslint-disable-next-line no-console
+					console.error("Failed to mark conversation as seen:", markSeenError);
+				}

As per coding guidelines.

apps/api/src/utils/conversation-realtime.ts (1)

67-69: Use exhaustive type checks for actor mapping

Prefer a typed assertUnreachable for compile‑time exhaustiveness and better error detail.

-function mapActor(actor: ConversationRealtimeActor) {
+function assertUnreachable(x: never): never {
+	throw new Error(`Unknown actor: ${JSON.stringify(x)}`);
+}
+
+function mapActor(actor: ConversationRealtimeActor) {
 	switch (actor.type) {
 		/* ...cases... */
-		default:
-			throw new Error("Unknown actor type");
+		default:
+			return assertUnreachable(actor as never);
 	}
}
packages/react/src/hooks/use-store-selector.ts (3)

1-1: Add useDebugValue for DevTools visibility

Expose the selected slice in React DevTools for easier debugging.

-import { useRef, useSyncExternalStore } from "react";
+import { useDebugValue, useRef, useSyncExternalStore } from "react";

29-36: Avoid treating undefined as a sentinel value

If a selector intentionally returns undefined, the current check reassigns on every render. Track initialization separately.

-	if (
-		selectionRef.current === undefined ||
-		!isEqual(selectionRef.current, selected)
-	) {
-		selectionRef.current = selected;
-	}
+	const hasSelectionRef = useRef(false);
+	if (!hasSelectionRef.current || !isEqual(selectionRef.current as TSelected, selected)) {
+		selectionRef.current = selected;
+		hasSelectionRef.current = true;
+	}

10-14: Add concise JSDoc with usage and cautions

Document the API and comparator semantics for consumers.

 export function useStoreSelector<TState, TSelected>(
 	store: BasicStore<TState>,
 	selector: (state: TState) => TSelected,
 	isEqual: (previous: TSelected, next: TSelected) => boolean = Object.is
 ): TSelected {
+	/**
+	 * Selects a memoized slice from an external store with anti-tearing guarantees via useSyncExternalStore.
+	 * - Provide a stable selector when possible.
+	 * - Supply a custom comparator for structural equality to reduce re-renders.
+	 */
packages/react/src/hooks/use-client-query.ts (3)

205-239: Skip registering focus/visibility listeners when disabled or no client.

Avoid unnecessary listeners when client is null or enabled is false.

 useEffect(() => {
-  if (
-    !refetchOnWindowFocus ||
-    typeof window === "undefined" ||
-    typeof document === "undefined"
-  ) {
+  if (
+    !(client && enabled) ||
+    !refetchOnWindowFocus ||
+    typeof window === "undefined" ||
+    typeof document === "undefined"
+  ) {
     return;
   }

188-195: Align types with guard (null check).

refetchInterval is typed number | false, but you also guard for null. Either extend the type to number | false | null or drop the null check. I suggest removing the redundant null branch.

-  if (
-    refetchInterval === false ||
-    refetchInterval === null ||
-    refetchInterval <= 0
-  ) {
+  if (refetchInterval === false || refetchInterval <= 0) {
     return;
   }

107-110: Remove the extra fetchId bump on mount.

This increment isn’t needed; execute already manages fetchIdRef. Dropping it reduces cognitive overhead without changing behavior.

-useEffect(() => {
-  fetchIdRef.current += 1;
-}, []);
packages/types/src/realtime-events.ts (2)

34-42: Use a datetime string for lastSeenAt to match API schemas.

Align with API (z.date() in conversation response) by using an ISO datetime string here.

 CONVERSATION_SEEN: z.object({
   conversationId: z.string(),
   websiteId: z.string(),
   organizationId: z.string(),
   visitorId: z.string().nullable(),
   userId: z.string().nullable(),
   aiAgentId: z.string().nullable(),
-  lastSeenAt: z.string(),
+  lastSeenAt: z.string().datetime(),
 }),

68-89: Tighten message type/visibility validation (optional).

If you have literal unions/constants for message type and visibility, prefer z.enum([...]) over z.string() for stronger validation (and drop the unused enum imports). If not available, keeping z.string() is acceptable.

packages/react/src/hooks/use-conversations-store.ts (1)

14-44: Deduplicate the equality comparator.

This areSelectionsEqual mirrors logic used elsewhere (e.g., support hooks). Extract a shared comparator (e.g., areConversationsSelectionEqual) to avoid divergence.

As per coding guidelines.

packages/core/src/store/messages-store.ts (1)

230-235: Optional: return a default empty shape.

Returning undefined forces callers to null-check. Consider returning an empty conversation state for ergonomics.

-export function getConversationMessages(
+export function getConversationMessages(
 	store: Store<MessagesState>,
 	conversationId: string
-): ConversationMessagesState | undefined {
-	return store.getState().conversations[conversationId];
+): ConversationMessagesState {
+	return (
+		store.getState().conversations[conversationId] ?? {
+			messages: [],
+			hasNextPage: false,
+			nextCursor: undefined,
+		}
+	);
 }

As per coding guidelines.

packages/core/src/store/conversations-store.ts (1)

94-101: Minor perf nit: avoid O(n·m) includes on large pages.

When page <= 1, building rest with incoming.includes(id) is quadratic. Use a Set for incoming.

-  if (page <= 1) {
-    const rest = existing.filter((id) => !incoming.includes(id));
+  if (page <= 1) {
+    const incomingSet = new Set(incoming);
+    const rest = existing.filter((id) => !incomingSet.has(id));
     const next = [...incoming, ...rest];
packages/react/src/support/hooks/use-conversations.ts (1)

176-185: Include all request filters in the query key to avoid stale caches.

Only a subset of fields from requestDefaults are included, which can miss additional filters and prevent refetches when those change.

Minimal change: append the whole requestDefaults object to the key (your useClientQuery hashes keys stably).

   queryKey: [
     "conversations",
     clientKey,
     requestDefaults.limit ?? null,
     requestDefaults.page ?? null,
     requestDefaults.status ?? null,
     requestDefaults.orderBy ?? null,
     requestDefaults.order ?? null,
+    requestDefaults,
   ],
packages/core/src/client.ts (1)

335-343: Update conversation lastMessage/updatedAt on realtime MESSAGE_CREATED.

Keep conversations list fresh when realtime events arrive.

   handleRealtimeEvent(event: RealtimeEvent): void {
     switch (event.type) {
       case "MESSAGE_CREATED":
-        this.messagesStore.ingestRealtime(event);
-        break;
+        this.messagesStore.ingestRealtime(event);
+        {
+          const message = (event as any).data?.message as Message | undefined;
+          if (message) {
+            const existing =
+              this.conversationsStore.getState().byId[message.conversationId];
+            if (existing) {
+              this.conversationsStore.ingestConversation({
+                ...existing,
+                lastMessage: message,
+                updatedAt: new Date(),
+              });
+            }
+          }
+        }
+        break;
       default:
         break;
     }
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54b5be5 and 20aec58.

📒 Files selected for processing (47)
  • apps/api/scripts/migrate.ts (0 hunks)
  • apps/api/src/db/cache/bun-redis-cache.ts (0 hunks)
  • apps/api/src/rest/routers/conversation.ts (2 hunks)
  • apps/api/src/rest/routers/visitor.ts (0 hunks)
  • apps/api/src/trpc/routers/conversation.ts (2 hunks)
  • apps/api/src/utils/conversation-realtime.ts (2 hunks)
  • apps/api/src/ws/realtime-pubsub.ts (0 hunks)
  • apps/api/src/ws/router.test.ts (1 hunks)
  • apps/web/src/components/conversation/index.tsx (1 hunks)
  • apps/web/src/components/conversation/messages/group.tsx (2 hunks)
  • apps/web/src/components/conversation/messages/list.tsx (2 hunks)
  • apps/web/src/components/conversation/messages/message.tsx (1 hunks)
  • apps/web/src/components/conversation/messages/read-indicator.tsx (2 hunks)
  • apps/web/src/components/ui/avatar.tsx (1 hunks)
  • apps/web/src/lib/metadata.ts (0 hunks)
  • apps/web/src/lib/trpc/server.tsx (0 hunks)
  • biome.jsonc (1 hunks)
  • packages/core/src/client.ts (5 hunks)
  • packages/core/src/index.ts (1 hunks)
  • packages/core/src/rest-client.ts (3 hunks)
  • packages/core/src/store/conversations-store.test.ts (1 hunks)
  • packages/core/src/store/conversations-store.ts (1 hunks)
  • packages/core/src/store/create-store.ts (1 hunks)
  • packages/core/src/store/messages-store.test.ts (1 hunks)
  • packages/core/src/store/messages-store.ts (1 hunks)
  • packages/core/src/store/website-store.test.ts (1 hunks)
  • packages/core/src/store/website-store.ts (1 hunks)
  • packages/react/src/hooks/index.ts (1 hunks)
  • packages/react/src/hooks/use-client-query.ts (1 hunks)
  • packages/react/src/hooks/use-conversation-messages.ts (1 hunks)
  • packages/react/src/hooks/use-conversations-store.ts (1 hunks)
  • packages/react/src/hooks/use-grouped-messages.ts (0 hunks)
  • packages/react/src/hooks/use-store-selector.ts (1 hunks)
  • packages/react/src/hooks/use-website-store.ts (1 hunks)
  • packages/react/src/index.ts (1 hunks)
  • packages/react/src/primitives/multimodal-input.tsx (0 hunks)
  • packages/react/src/provider.tsx (2 hunks)
  • packages/react/src/support/hooks/index.ts (1 hunks)
  • packages/react/src/support/hooks/use-conversation.ts (1 hunks)
  • packages/react/src/support/hooks/use-conversations.ts (1 hunks)
  • packages/react/src/support/hooks/use-website.ts (1 hunks)
  • packages/react/src/support/pages/conversation-history.tsx (1 hunks)
  • packages/react/src/support/pages/conversation.tsx (2 hunks)
  • packages/react/src/support/pages/home.tsx (1 hunks)
  • packages/react/src/utils/message-converter.ts (0 hunks)
  • packages/types/src/api/conversation.ts (1 hunks)
  • packages/types/src/realtime-events.ts (1 hunks)
💤 Files with no reviewable changes (9)
  • apps/api/src/db/cache/bun-redis-cache.ts
  • packages/react/src/utils/message-converter.ts
  • apps/api/src/rest/routers/visitor.ts
  • apps/api/scripts/migrate.ts
  • apps/web/src/lib/metadata.ts
  • apps/api/src/ws/realtime-pubsub.ts
  • apps/web/src/lib/trpc/server.tsx
  • packages/react/src/primitives/multimodal-input.tsx
  • packages/react/src/hooks/use-grouped-messages.ts
✅ Files skipped from review due to trivial changes (6)
  • apps/api/src/ws/router.test.ts
  • apps/api/src/trpc/routers/conversation.ts
  • apps/web/src/components/ui/avatar.tsx
  • apps/web/src/components/conversation/messages/list.tsx
  • apps/web/src/components/conversation/messages/read-indicator.tsx
  • apps/web/src/components/conversation/messages/group.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/react/src/hooks/index.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/support/hooks/use-website.ts
  • packages/react/src/hooks/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/core/src/store/website-store.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/support/pages/conversation-history.tsx
🧰 Additional context used
📓 Path-based instructions (19)
**/*.{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/core/src/store/conversations-store.test.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/types/src/realtime-events.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/core/src/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/core/src/client.ts
  • apps/web/src/components/conversation/messages/message.tsx
  • packages/core/src/rest-client.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/types/src/api/conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • apps/api/src/utils/conversation-realtime.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/react/src/index.ts
  • apps/web/src/components/conversation/index.tsx
**/*.{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:

  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/store/website-store.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:

  • packages/core/src/store/conversations-store.test.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/types/src/realtime-events.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/core/src/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/core/src/client.ts
  • apps/web/src/components/conversation/messages/message.tsx
  • packages/core/src/rest-client.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/types/src/api/conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • apps/api/src/utils/conversation-realtime.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/react/src/index.ts
  • apps/web/src/components/conversation/index.tsx
**/*.{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/core/src/store/conversations-store.test.ts
  • packages/types/src/realtime-events.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/core/src/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/core/src/client.ts
  • packages/core/src/rest-client.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/types/src/api/conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • apps/api/src/utils/conversation-realtime.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/react/src/index.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:

  • packages/core/src/store/conversations-store.test.ts
  • packages/core/src/store/website-store.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:

  • packages/core/src/store/conversations-store.test.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/types/src/realtime-events.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/core/src/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/core/src/client.ts
  • apps/web/src/components/conversation/messages/message.tsx
  • packages/core/src/rest-client.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/types/src/api/conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • apps/api/src/utils/conversation-realtime.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/react/src/index.ts
  • apps/web/src/components/conversation/index.tsx
**/*.{js,tsx,ts,jsx}

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

Use arrow functions instead of function expressions

Files:

  • packages/core/src/store/conversations-store.test.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/types/src/realtime-events.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/core/src/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/core/src/client.ts
  • apps/web/src/components/conversation/messages/message.tsx
  • packages/core/src/rest-client.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/types/src/api/conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • apps/api/src/utils/conversation-realtime.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/react/src/index.ts
  • apps/web/src/components/conversation/index.tsx
**/*.{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/core/src/store/conversations-store.test.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/types/src/realtime-events.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/core/src/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/core/src/client.ts
  • apps/web/src/components/conversation/messages/message.tsx
  • packages/core/src/rest-client.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/types/src/api/conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • apps/api/src/utils/conversation-realtime.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/react/src/index.ts
  • apps/web/src/components/conversation/index.tsx
**/*.{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/core/src/store/conversations-store.test.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/types/src/realtime-events.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • apps/api/src/rest/routers/conversation.ts
  • packages/core/src/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/core/src/client.ts
  • apps/web/src/components/conversation/messages/message.tsx
  • packages/core/src/rest-client.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/types/src/api/conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • apps/api/src/utils/conversation-realtime.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/react/src/index.ts
  • apps/web/src/components/conversation/index.tsx
packages/**

📄 CodeRabbit inference engine (AGENTS.md)

Put shared contracts and utilities in packages/* (e.g., core, types, transactional)

Files:

  • packages/core/src/store/conversations-store.test.ts
  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/types/src/realtime-events.ts
  • packages/core/src/store/messages-store.ts
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/core/src/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/core/src/client.ts
  • packages/core/src/rest-client.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/core/src/store/create-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/types/src/api/conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/core/src/store/conversations-store.ts
  • packages/core/src/store/website-store.test.ts
  • packages/react/src/index.ts
packages/react/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Develop headless React components (including and primitives) under packages/react/src/

Files:

  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/index.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:

  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • apps/web/src/components/conversation/messages/message.tsx
  • apps/web/src/components/conversation/index.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:

  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • apps/web/src/components/conversation/messages/message.tsx
  • apps/web/src/components/conversation/index.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:

  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • apps/web/src/components/conversation/messages/message.tsx
  • apps/web/src/components/conversation/index.tsx
packages/react/**/*.{ts,tsx}

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

packages/react/**/*.{ts,tsx}: Components must render with no built-in styles (headless, unstyled)
Components must accept className, style, and asChild props to enable external styling/control
Do not use emotion, styled-components, or style-prop APIs; styling is via Tailwind/CSS through className/style
Use useRenderElement to create elements and encapsulate render logic (supporting intrinsic tags and render prop overrides)
Components with internal state must be controllable via props (e.g., value/defaultValue/onChange or isOpen/onOpenChange)
Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child
May reference Base UI logic internally, but never expose Base UI or MUI APIs; do not leak getRootProps/getInputProps; prefer useRenderElement
Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components
Provide a render prop API: render?: (props, state) => ReactElement, and allow className to be string or (state) => string
Support developer-owned rendering: components should not impose visual structure and should allow parent-driven markup via render prop/asChild

Files:

  • packages/react/src/support/pages/home.tsx
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations-store.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/use-store-selector.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-conversations.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/index.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
  • packages/types/src/api/conversation.ts
packages/types/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Surface shared DTOs in packages/types

Files:

  • packages/types/src/realtime-events.ts
  • packages/types/src/api/conversation.ts
apps/web/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Put reusable React components under apps/web/src/components

Files:

  • apps/web/src/components/conversation/messages/message.tsx
  • apps/web/src/components/conversation/index.tsx
apps/web/src/{app,components}/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Use snake_case filenames for React components and layouts in the web app

Files:

  • apps/web/src/components/conversation/messages/message.tsx
  • apps/web/src/components/conversation/index.tsx
🧠 Learnings (5)
📚 Learning: 2025-09-17T16:59:53.676Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/react-package-rules.mdc:0-0
Timestamp: 2025-09-17T16:59:53.676Z
Learning: Applies to packages/react/**/*.{ts,tsx} : Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components

Applied to files:

  • apps/web/src/components/conversation/messages/message.tsx
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{js,ts} : Ensure Promise-like statements are handled (awaited, returned, or chained) appropriately

Applied to files:

  • packages/react/src/hooks/use-client-query.ts
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{js,ts} : Don't use async functions as Promise executors

Applied to files:

  • packages/react/src/hooks/use-client-query.ts
📚 Learning: 2025-09-17T09:00:28.751Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T09:00:28.751Z
Learning: Applies to packages/react/src/**/*.{ts,tsx} : Develop headless React components (including <Support /> and primitives) under packages/react/src/

Applied to files:

  • packages/react/src/index.ts
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to **/*.{ts,tsx} : Prefer explicit return types on exported APIs in TypeScript

Applied to files:

  • packages/react/src/index.ts
🧬 Code graph analysis (16)
packages/core/src/store/conversations-store.test.ts (2)
packages/core/src/store/conversations-store.ts (3)
  • createConversationsStore (195-209)
  • getConversations (211-220)
  • getConversationById (222-227)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
packages/react/src/support/pages/home.tsx (2)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/support/hooks/use-conversations.ts (1)
  • useConversations (111-215)
packages/core/src/store/messages-store.ts (4)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (94-98)
packages/core/src/store/create-store.ts (2)
  • Store (7-12)
  • createStore (14-69)
packages/core/src/client.ts (1)
  • getConversationMessages (232-242)
packages/core/src/rest-client.ts (1)
  • getConversationMessages (514-574)
packages/react/src/hooks/use-conversations-store.ts (3)
packages/core/src/store/conversations-store.ts (2)
  • ConversationPagination (5-5)
  • ConversationsState (7-11)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-37)
apps/api/src/rest/routers/conversation.ts (7)
packages/types/src/api/conversation.ts (8)
  • createConversationRequestSchema (5-33)
  • createConversationResponseSchema (39-46)
  • listConversationsRequestSchema (52-88)
  • listConversationsResponseSchema (94-107)
  • getConversationRequestSchema (113-121)
  • getConversationResponseSchema (127-133)
  • markConversationSeenRequestSchema (139-153)
  • markConversationSeenResponseSchema (159-171)
apps/api/src/utils/validate.ts (3)
  • safelyExtractRequestData (56-107)
  • validateResponse (6-20)
  • safelyExtractRequestQuery (120-162)
apps/api/src/db/queries/visitor.ts (1)
  • getVisitor (104-133)
apps/api/src/db/queries/conversation.ts (3)
  • upsertConversation (37-66)
  • listConversations (68-174)
  • getConversationByIdWithLastMessage (176-219)
apps/api/src/utils/message.ts (1)
  • createMessage (79-141)
apps/api/src/db/mutations/conversation.ts (1)
  • markConversationAsSeenByVisitor (341-372)
apps/api/src/utils/conversation-realtime.ts (1)
  • emitConversationSeenEvent (72-101)
packages/react/src/hooks/use-conversation-messages.ts (4)
packages/core/src/store/messages-store.ts (2)
  • ConversationMessagesState (6-10)
  • MessagesState (12-14)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/use-client-query.ts (1)
  • useClientQuery (52-254)
packages/core/src/client.ts (7)
packages/core/src/rest-client.ts (2)
  • CossistantRestClient (43-623)
  • request (149-173)
packages/core/src/store/conversations-store.ts (2)
  • ConversationsStore (190-193)
  • createConversationsStore (195-209)
packages/core/src/store/messages-store.ts (2)
  • MessagesStore (188-198)
  • createMessagesStore (200-228)
packages/core/src/store/website-store.ts (3)
  • WebsiteStore (39-44)
  • createWebsiteStore (46-105)
  • WebsiteState (10-14)
packages/core/src/utils.ts (2)
  • generateConversationId (13-16)
  • generateMessageId (18-20)
packages/types/src/enums.ts (6)
  • ConversationStatus (17-21)
  • ConversationStatus (23-24)
  • MessageType (1-5)
  • MessageType (7-7)
  • MessageVisibility (33-36)
  • MessageVisibility (74-75)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (94-98)
apps/web/src/components/conversation/messages/message.tsx (2)
packages/react/src/primitives/message.tsx (4)
  • MessageProps (19-27)
  • Message (33-81)
  • MessageContent (157-207)
  • MessageTimestamp (224-267)
packages/react/src/support/components/message.tsx (2)
  • MessageProps (10-14)
  • Message (16-77)
packages/core/src/rest-client.ts (1)
packages/core/src/visitor-tracker.ts (1)
  • getVisitorId (36-59)
packages/react/src/hooks/use-store-selector.ts (1)
packages/core/src/store/create-store.ts (1)
  • subscribe (50-55)
packages/react/src/support/hooks/use-conversation.ts (5)
packages/core/src/store/conversations-store.ts (1)
  • ConversationsState (7-11)
packages/types/src/api/conversation.ts (2)
  • GetConversationResponse (135-137)
  • GetConversationRequest (123-125)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/use-client-query.ts (1)
  • useClientQuery (52-254)
packages/react/src/support/hooks/use-conversations.ts (5)
packages/core/src/store/conversations-store.ts (2)
  • ConversationPagination (5-5)
  • ConversationsState (7-11)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
packages/react/src/provider.tsx (1)
  • useSupport (227-244)
packages/react/src/hooks/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/use-client-query.ts (1)
  • useClientQuery (52-254)
packages/react/src/hooks/use-client-query.ts (1)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
packages/core/src/store/conversations-store.ts (2)
packages/types/src/api/conversation.ts (1)
  • ListConversationsResponse (109-111)
packages/core/src/store/create-store.ts (2)
  • Store (7-12)
  • createStore (14-69)
packages/core/src/store/website-store.test.ts (2)
packages/core/src/store/website-store.ts (2)
  • createWebsiteStore (46-105)
  • WebsiteState (10-14)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
apps/web/src/components/conversation/index.tsx (11)
apps/web/src/hooks/use-send-conversation-message.ts (1)
  • useSendConversationMessage (34-153)
apps/web/src/contexts/website.tsx (1)
  • useWebsiteMembers (128-135)
apps/web/src/contexts/inboxes/index.tsx (2)
  • useInboxes (113-125)
  • ConversationHeader (12-13)
apps/web/src/data/use-conversation-actions.ts (1)
  • useConversationActions (102-705)
apps/web/src/data/use-conversation-messages.tsx (1)
  • useConversationMessages (17-68)
apps/web/src/data/use-conversation-events.tsx (1)
  • useConversationEvents (16-66)
apps/web/src/data/use-visitor.tsx (1)
  • useVisitor (6-37)
apps/web/src/components/ui/layout/index.tsx (1)
  • Page (49-66)
apps/web/src/components/conversation/messages/list.tsx (1)
  • MessagesList (33-120)
apps/web/src/components/conversation/multimodal-input/index.tsx (1)
  • MultimodalInput (28-180)
apps/web/src/components/ui/layout/sidebars/visitor/visitor-sidebar.tsx (1)
  • VisitorSidebar (16-99)
⏰ 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

Comment on lines +535 to +546
const { db, website, organization, body, visitorIdHeader } =
await safelyExtractRequestData(c, markConversationSeenRequestSchema);

const params = getConversationRequestSchema.parse({
conversationId: c.req.param("conversationId"),
});
const params = getConversationRequestSchema.parse({
conversationId: c.req.param("conversationId"),
});

const [visitor, conversationRecord] = await Promise.all([
getVisitor(db, {
visitorId: body.visitorId || visitorIdHeader,
externalVisitorId: body.externalVisitorId,
}),
getConversationByIdWithLastMessage(db, {
organizationId: organization.id,
websiteId: website.id,
conversationId: params.conversationId,
}),
]);
const [visitor, conversationRecord] = await Promise.all([
getVisitor(db, {
visitorId: body.visitorId || visitorIdHeader,
externalVisitorId: body.externalVisitorId,
}),

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 | 🟠 Major

Empty POST body now triggers 400 on the “seen” route

markConversationSeen documents the JSON body as optional, but the new call to safelyExtractRequestData(c, markConversationSeenRequestSchema) always forces a JSON parse. With the Fetch API (and Hono’s Request.json), posting with no body — e.g. fetch("/seen", { method: "POST" }) — throws SyntaxError: Unexpected end of JSON input, so we now return the helper’s 400 “Invalid JSON” error for perfectly valid requests. That’s a regression for any client relying solely on the X-Visitor-Id header.

Guard the parse so we only invoke the schema path when a body is actually present, and fall back to an empty object otherwise:

-	const { db, website, organization, body, visitorIdHeader } =
-		await safelyExtractRequestData(c, markConversationSeenRequestSchema);
+	const hasBody = c.req.raw.body !== null;
+
+	const {
+		db,
+		website,
+		organization,
+		body,
+		visitorIdHeader,
+	} = hasBody
+		? await safelyExtractRequestData(c, markConversationSeenRequestSchema)
+		: await safelyExtractRequestData(c);
+
+	const payload =
+		hasBody && body
+			? body
+			: markConversationSeenRequestSchema.parse({});
@@
-			getVisitor(db, {
-				visitorId: body.visitorId || visitorIdHeader,
-				externalVisitorId: body.externalVisitorId,
+			getVisitor(db, {
+				visitorId: payload.visitorId || visitorIdHeader,
+				externalVisitorId: payload.externalVisitorId,

That preserves validation when JSON is supplied while keeping the route usable without a body.

📝 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
const { db, website, organization, body, visitorIdHeader } =
await safelyExtractRequestData(c, markConversationSeenRequestSchema);
const params = getConversationRequestSchema.parse({
conversationId: c.req.param("conversationId"),
});
const params = getConversationRequestSchema.parse({
conversationId: c.req.param("conversationId"),
});
const [visitor, conversationRecord] = await Promise.all([
getVisitor(db, {
visitorId: body.visitorId || visitorIdHeader,
externalVisitorId: body.externalVisitorId,
}),
getConversationByIdWithLastMessage(db, {
organizationId: organization.id,
websiteId: website.id,
conversationId: params.conversationId,
}),
]);
const [visitor, conversationRecord] = await Promise.all([
getVisitor(db, {
visitorId: body.visitorId || visitorIdHeader,
externalVisitorId: body.externalVisitorId,
}),
// Determine whether a JSON body was actually sent
const hasBody = c.req.raw.body !== null;
// Extract request data, validating only if a body is present
const {
db,
website,
organization,
body,
visitorIdHeader,
} = hasBody
? await safelyExtractRequestData(c, markConversationSeenRequestSchema)
: await safelyExtractRequestData(c);
// Normalize to a payload object, falling back to an empty schema-validated object
const payload =
hasBody && body
? body
: markConversationSeenRequestSchema.parse({});
const params = getConversationRequestSchema.parse({
conversationId: c.req.param("conversationId"),
});
const [visitor, conversationRecord] = await Promise.all([
getVisitor(db, {
visitorId: payload.visitorId || visitorIdHeader,
externalVisitorId: payload.externalVisitorId,
}),
// …
]);
🤖 Prompt for AI Agents
In apps/api/src/rest/routers/conversation.ts around lines 535–546, avoid calling
safelyExtractRequestData unconditionally because an empty POST body causes
Request.json() to throw; instead, first check the incoming request body (e.g.,
read raw text or inspect Content-Length/header) and if it is empty use an empty
object for body and still extract visitorIdHeader from headers, otherwise call
safelyExtractRequestData(c, markConversationSeenRequestSchema) to validate/parse
the JSON; update subsequent code to use the resulting { db, website,
organization, body, visitorIdHeader } so validation still occurs when JSON is
provided but empty POSTs relying on headers succeed without a 400.

Comment on lines +151 to +166
const seenData = selectedConversation?.seenData ?? [];

console.log("seenData", seenData);

return (
<>
<Page className="relative px-[1px] py-0">
<div className="pointer-events-none absolute inset-x-0 top-0 z-0 h-14 bg-gradient-to-b from-co-background/50 to-transparent dark:from-co-background-100/80" />
<ConversationHeader />
<MessagesList
availableAIAgents={[]}
currentUserId={currentUserId}
events={events}
messages={messages}
onFetchMoreIfNeeded={onFetchMoreIfNeeded}
seenData={selectedConversation?.seenData ?? []}

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 | 🟡 Minor

Remove debug log and use the computed seenData variable

Avoid console noise and pass the prepared value consistently.

Apply:

   const seenData = selectedConversation?.seenData ?? [];
-
-  console.log("seenData", seenData);
@@
-          seenData={selectedConversation?.seenData ?? []}
+          seenData={seenData}

As per coding guidelines (observability hygiene).

📝 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
const seenData = selectedConversation?.seenData ?? [];
console.log("seenData", seenData);
return (
<>
<Page className="relative px-[1px] py-0">
<div className="pointer-events-none absolute inset-x-0 top-0 z-0 h-14 bg-gradient-to-b from-co-background/50 to-transparent dark:from-co-background-100/80" />
<ConversationHeader />
<MessagesList
availableAIAgents={[]}
currentUserId={currentUserId}
events={events}
messages={messages}
onFetchMoreIfNeeded={onFetchMoreIfNeeded}
seenData={selectedConversation?.seenData ?? []}
const seenData = selectedConversation?.seenData ?? [];
return (
<>
<Page className="relative px-[1px] py-0">
<div className="pointer-events-none absolute inset-x-0 top-0 z-0 h-14 bg-gradient-to-b from-co-background/50 to-transparent dark:from-co-background-100/80" />
<ConversationHeader />
<MessagesList
availableAIAgents={[]}
currentUserId={currentUserId}
events={events}
messages={messages}
onFetchMoreIfNeeded={onFetchMoreIfNeeded}
seenData={seenData}
🤖 Prompt for AI Agents
In apps/web/src/components/conversation/index.tsx around lines 151 to 166,
there's a leftover console.log("seenData", seenData) and the MessagesList prop
uses selectedConversation?.seenData ?? [] instead of the computed seenData
variable; remove the console.log and replace the inline expression with the
seenData variable so the prepared value is passed consistently and there is no
debug noise.

Comment on lines +171 to +179
allowedFileTypes={["image/*", "application/pdf", "text/*"]}
error={error}
files={files}
isSubmitting={isSubmitting}
maxFileSize={10 * 1024 * 1024}
maxFiles={2}
onChange={setMessage}
onFileSelect={addFiles}
onRemoveFile={removeFile}

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 | 🟠 Major

Hide file attachments UI until sending files is supported

Backend path throws “File attachments are not supported yet.” Presenting attach UI creates a broken path.

Apply:

-          maxFiles={2}
+          maxFiles={0}
           onChange={setMessage}
-          onFileSelect={addFiles}
-          onRemoveFile={removeFile}
           onSubmit={submit}

This pairs with removing addFiles/removeFile from the destructuring above.

📝 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
allowedFileTypes={["image/*", "application/pdf", "text/*"]}
error={error}
files={files}
isSubmitting={isSubmitting}
maxFileSize={10 * 1024 * 1024}
maxFiles={2}
onChange={setMessage}
onFileSelect={addFiles}
onRemoveFile={removeFile}
allowedFileTypes={["image/*", "application/pdf", "text/*"]}
error={error}
files={files}
isSubmitting={isSubmitting}
maxFileSize={10 * 1024 * 1024}
maxFiles={0}
onChange={setMessage}
onSubmit={submit}
🤖 Prompt for AI Agents
In apps/web/src/components/conversation/index.tsx around lines 171-179, the file
attachment UI is being rendered even though the backend returns "File
attachments are not supported yet." Remove or hide the attachment-related UI by
not rendering the attachment component (or wrapping it in a feature-flag/boolean
that is currently false) and remove addFiles and removeFile from the props/state
destructuring earlier in the file so they are not referenced; keep other props
(allowedFileTypes, error, files, isSubmitting, maxFileSize, maxFiles, onChange)
only if needed, or remove all attachment-related props entirely until backend
support is added.

Comment on lines 16 to +71
export function Message({
message,
isLast = false,
isSentByViewer = false,
message,
isLast = false,
isSentByViewer = false,
}: MessageProps) {
return (
<PrimitiveMessage message={message}>
{({ isAI, timestamp }) => (
<div
className={cn(
"flex w-full gap-2",
isSentByViewer && "flex-row-reverse",
!isSentByViewer && "flex-row"
)}
>
<div
className={cn(
"flex w-full flex-1 flex-col gap-3",
isSentByViewer && "items-end"
)}
>
<MessageContent
bodyMd={message.bodyMd}
className={cn(
"block max-w-full rounded-lg px-3 py-2 text-sm md:w-max md:max-w-[420px]",
{
"bg-co-background-300 text-foreground dark:bg-co-background-600":
!isSentByViewer,
"bg-primary text-primary-foreground": isSentByViewer,
"rounded-br-[2px]": isLast && isSentByViewer,
"rounded-bl-[2px]": isLast && !isSentByViewer,
}
)}
renderMarkdown
/>
{isLast && (
<MessageTimestamp
className="px-1 text-muted-foreground text-xs"
timestamp={timestamp}
>
{() => (
<>
{timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
{isAI && " • AI agent"}
</>
)}
</MessageTimestamp>
)}
</div>
</div>
)}
</PrimitiveMessage>
);
return (
<PrimitiveMessage message={message}>
{({ isAI, timestamp }) => (
<div
className={cn(
"flex w-full gap-2",
isSentByViewer && "flex-row-reverse",
!isSentByViewer && "flex-row"
)}
>
<div
className={cn(
"flex w-full flex-1 flex-col gap-3",
isSentByViewer && "items-end"
)}
>
<MessageContent
bodyMd={message.bodyMd}
className={cn(
"block max-w-full rounded-lg px-3 py-2 text-sm md:w-max md:max-w-[420px]",
{
"bg-co-background-300 text-foreground dark:bg-co-background-600":
!isSentByViewer,
"bg-primary text-primary-foreground": isSentByViewer,
"rounded-br-[2px]": isLast && isSentByViewer,
"rounded-bl-[2px]": isLast && !isSentByViewer,
}
)}
renderMarkdown
/>
{isLast && (
<MessageTimestamp
className="px-1 text-muted-foreground text-xs"
timestamp={timestamp}
>
{() => (
<>
{timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
{isAI && " • AI agent"}
</>
)}
</MessageTimestamp>
)}
</div>
</div>
)}
</PrimitiveMessage>
);

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 | 🟠 Major

Infer isSentByViewer from render‑props when not provided

Without a fallback to the message sender, viewer messages can render on the wrong side when isSentByViewer isn’t passed. Use the isVisitor flag from PrimitiveMessage to derive a sane default.

Apply:

-export function Message({
-	message,
-	isLast = false,
-	isSentByViewer = false,
-}: MessageProps) {
+export function Message({ message, isLast = false, isSentByViewer }: MessageProps) {
 	return (
 		<PrimitiveMessage message={message}>
-			{({ isAI, timestamp }) => (
+			{({ isAI, isVisitor, timestamp }) => {
+				const isSentByViewerFinal = isSentByViewer ?? isVisitor;
+				return (
 				<div
 					className={cn(
 						"flex w-full gap-2",
-						isSentByViewer && "flex-row-reverse",
-						!isSentByViewer && "flex-row"
+						isSentByViewerFinal && "flex-row-reverse",
+						!isSentByViewerFinal && "flex-row"
 					)}
 				>
 					<div
 						className={cn(
 							"flex w-full flex-1 flex-col gap-3",
-							isSentByViewer && "items-end"
+							isSentByViewerFinal && "items-end"
 						)}
 					>
 						<MessageContent
 							bodyMd={message.bodyMd}
 							className={cn(
 								"block max-w-full rounded-lg px-3 py-2 text-sm md:w-max md:max-w-[420px]",
 								{
 									"bg-co-background-300 text-foreground dark:bg-co-background-600":
-										!isSentByViewer,
-									"bg-primary text-primary-foreground": isSentByViewer,
-									"rounded-br-[2px]": isLast && isSentByViewer,
-									"rounded-bl-[2px]": isLast && !isSentByViewer,
+										!isSentByViewerFinal,
+									"bg-primary text-primary-foreground": isSentByViewerFinal,
+									"rounded-br-[2px]": isLast && isSentByViewerFinal,
+									"rounded-bl-[2px]": isLast && !isSentByViewerFinal,
 								}
 							)}
 							renderMarkdown
 						/>
 						{isLast && (
 							<MessageTimestamp
 								className="px-1 text-muted-foreground text-xs"
 								timestamp={timestamp}
 							>
-								{() => (
+								{() => (
 									<>
 										{timestamp.toLocaleTimeString([], {
 											hour: "2-digit",
 											minute: "2-digit",
 										})}
 										{isAI && " • AI agent"}
 									</>
 								)}
 							</MessageTimestamp>
 						)}
 					</div>
 				</div>
-			)}
+			)}}
 		</PrimitiveMessage>
 	);

Based on learnings.

📝 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
export function Message({
message,
isLast = false,
isSentByViewer = false,
message,
isLast = false,
isSentByViewer = false,
}: MessageProps) {
return (
<PrimitiveMessage message={message}>
{({ isAI, timestamp }) => (
<div
className={cn(
"flex w-full gap-2",
isSentByViewer && "flex-row-reverse",
!isSentByViewer && "flex-row"
)}
>
<div
className={cn(
"flex w-full flex-1 flex-col gap-3",
isSentByViewer && "items-end"
)}
>
<MessageContent
bodyMd={message.bodyMd}
className={cn(
"block max-w-full rounded-lg px-3 py-2 text-sm md:w-max md:max-w-[420px]",
{
"bg-co-background-300 text-foreground dark:bg-co-background-600":
!isSentByViewer,
"bg-primary text-primary-foreground": isSentByViewer,
"rounded-br-[2px]": isLast && isSentByViewer,
"rounded-bl-[2px]": isLast && !isSentByViewer,
}
)}
renderMarkdown
/>
{isLast && (
<MessageTimestamp
className="px-1 text-muted-foreground text-xs"
timestamp={timestamp}
>
{() => (
<>
{timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
{isAI && " • AI agent"}
</>
)}
</MessageTimestamp>
)}
</div>
</div>
)}
</PrimitiveMessage>
);
return (
<PrimitiveMessage message={message}>
{({ isAI, timestamp }) => (
<div
className={cn(
"flex w-full gap-2",
isSentByViewer && "flex-row-reverse",
!isSentByViewer && "flex-row"
)}
>
<div
className={cn(
"flex w-full flex-1 flex-col gap-3",
isSentByViewer && "items-end"
)}
>
<MessageContent
bodyMd={message.bodyMd}
className={cn(
"block max-w-full rounded-lg px-3 py-2 text-sm md:w-max md:max-w-[420px]",
{
"bg-co-background-300 text-foreground dark:bg-co-background-600":
!isSentByViewer,
"bg-primary text-primary-foreground": isSentByViewer,
"rounded-br-[2px]": isLast && isSentByViewer,
"rounded-bl-[2px]": isLast && !isSentByViewer,
}
)}
renderMarkdown
/>
{isLast && (
<MessageTimestamp
className="px-1 text-muted-foreground text-xs"
timestamp={timestamp}
>
{() => (
<>
{timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
{isAI && " • AI agent"}
</>
)}
</MessageTimestamp>
)}
</div>
</div>
)}
</PrimitiveMessage>
);
export function Message({ message, isLast = false, isSentByViewer }: MessageProps) {
return (
<PrimitiveMessage message={message}>
{({ isAI, isVisitor, timestamp }) => {
const isSentByViewerFinal = isSentByViewer ?? isVisitor;
return (
<div
className={cn(
"flex w-full gap-2",
isSentByViewerFinal && "flex-row-reverse",
!isSentByViewerFinal && "flex-row"
)}
>
<div
className={cn(
"flex w-full flex-1 flex-col gap-3",
isSentByViewerFinal && "items-end"
)}
>
<MessageContent
bodyMd={message.bodyMd}
className={cn(
"block max-w-full rounded-lg px-3 py-2 text-sm md:w-max md:max-w-[420px]",
{
"bg-co-background-300 text-foreground dark:bg-co-background-600":
!isSentByViewerFinal,
"bg-primary text-primary-foreground": isSentByViewerFinal,
"rounded-br-[2px]": isLast && isSentByViewerFinal,
"rounded-bl-[2px]": isLast && !isSentByViewerFinal,
}
)}
renderMarkdown
/>
{isLast && (
<MessageTimestamp
className="px-1 text-muted-foreground text-xs"
timestamp={timestamp}
>
{() => (
<>
{timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
{isAI && " • AI agent"}
</>
)}
</MessageTimestamp>
)}
</div>
</div>
);
}}
</PrimitiveMessage>
);
}
🤖 Prompt for AI Agents
In apps/web/src/components/conversation/messages/message.tsx around lines 16 to
71, the UI currently relies solely on the isSentByViewer prop so when it’s
omitted messages can appear on the wrong side; use the PrimitiveMessage
render-props isVisitor flag as a fallback. Inside the PrimitiveMessage child,
destructure isVisitor from the render props and compute a local sentByViewer =
isSentByViewer ?? isVisitor (or similar), then replace all uses of
isSentByViewer in the JSX with that local variable so the component defaults to
the message sender when the prop is not provided.

Comment on lines +60 to +61
isSameMessage(a.lastMessage ?? undefined, b.lastMessage ?? undefined)
);

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 | 🟡 Minor

Drop unnecessary nullish coalescing to undefined.

Passing undefined widens the type and can violate Conversation["lastMessage"] if it is Message | null. Just pass the fields directly.

-    isSameMessage(a.lastMessage ?? undefined, b.lastMessage ?? undefined)
+    isSameMessage(a.lastMessage, b.lastMessage)
📝 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
isSameMessage(a.lastMessage ?? undefined, b.lastMessage ?? undefined)
);
isSameMessage(a.lastMessage, b.lastMessage)
);
🤖 Prompt for AI Agents
In packages/core/src/store/conversations-store.ts around lines 60 to 61, the
code uses unnecessary nullish coalescing to undefined when calling
isSameMessage(a.lastMessage ?? undefined, b.lastMessage ?? undefined); remove
the "?? undefined" so you pass a.lastMessage and b.lastMessage directly, which
preserves the original types (e.g., Message | null) and avoids widening the type
to include undefined.

Comment on lines +111 to +126
export function useConversations(
clientOrOptions?: CossistantClient | null | UseConversationsOptions,
maybeOptions?: UseConversationsOptions
): UseConversationsResult {
const isEnabled = !!client && params.enabled !== false;

const { data, isLoading, error } = useQuery({
queryKey: QUERY_KEYS.conversations(),
queryFn: async () => {
if (!client) {
throw new Error("No client available");
}

return client.listConversations({
limit: params.limit || 10,
orderBy: "createdAt",
order: "asc",
});
},
enabled: isEnabled,
staleTime: 5 * 60 * 1000,
const { client } = useSupport();
let resolvedOptions: UseConversationsOptions;

if (isClientLike(clientOrOptions) || clientOrOptions === null) {
resolvedOptions = { ...DEFAULT_OPTIONS, ...(maybeOptions ?? {}) };
} else {
resolvedOptions = { ...DEFAULT_OPTIONS, ...(clientOrOptions ?? {}) };
}

const store = client?.conversationsStore ?? EMPTY_STORE;

const selection = useStoreSelector(

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 | 🔴 Critical

Explicit client argument is ignored; respect the overload and use an effectiveClient.

Currently the hook always uses the provider client and never the explicit client passed via the overload. This breaks the public API and makes tests/usages that pass a client impossible.

Apply this diff to resolve the effective client and use it throughout:

@@
-export function useConversations(
+export function useConversations(
   clientOrOptions?: CossistantClient | null | UseConversationsOptions,
   maybeOptions?: UseConversationsOptions
 ): UseConversationsResult {
-  const { client } = useSupport();
-  let resolvedOptions: UseConversationsOptions;
+  const support = useSupport();
+  const providerClient = support.client;
+  let resolvedOptions: UseConversationsOptions;
+  const effectiveClient: CossistantClient | null =
+    isClientLike(clientOrOptions) || clientOrOptions === null
+      ? (clientOrOptions as CossistantClient | null)
+      : providerClient;
@@
-  if (isClientLike(clientOrOptions) || clientOrOptions === null) {
+  if (isClientLike(clientOrOptions) || clientOrOptions === null) {
     resolvedOptions = { ...DEFAULT_OPTIONS, ...(maybeOptions ?? {}) };
   } else {
     resolvedOptions = { ...DEFAULT_OPTIONS, ...(clientOrOptions ?? {}) };
   }
@@
-  const store = client?.conversationsStore ?? EMPTY_STORE;
+  const store = effectiveClient?.conversationsStore ?? EMPTY_STORE;
@@
-  const {
+  const {
     refetch: queryRefetch,
     isLoading: queryLoading,
     error,
   } = useClientQuery<
     ListConversationsResponse,
     Partial<ListConversationsRequest>
   >({
-    client,
+    client: effectiveClient,
@@
-    enabled: Boolean(client && enabled),
+    enabled: Boolean(effectiveClient && enabled),
     refetchInterval,
     refetchOnWindowFocus,
     refetchOnMount: selection.conversations.length === 0,
     initialArgs: requestDefaults,
   });

Also applies to: 124-141, 167-195, 197-204

🤖 Prompt for AI Agents
In packages/react/src/support/hooks/use-conversations.ts around lines 111 to 126
(and also apply the same change at 124-141, 167-195, 197-204), the hook ignores
an explicit client passed via the overload and always uses the provider client;
resolve an effectiveClient variable that picks the explicit client when
clientOrOptions is a client-like value (or null handling as intended) and
otherwise falls back to the provider client, then use that effectiveClient
everywhere the code currently uses client (including obtaining the
conversationsStore, selectors, and any operations) so the overload is respected
and explicit clients work in tests/usages.

Comment on lines 153 to 157
React.useEffect(() => {
lastSeenMessageIdRef.current = null;
markSeenInFlightRef.current = false;
}, [realConversationId]);
}, []);

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 | 🟠 Major

Reset seen tracking when conversation changes, not only on mount

Mount‑only reset can leak the last seen id across conversation switches.

Apply:

-	React.useEffect(() => {
-		lastSeenMessageIdRef.current = null;
-		markSeenInFlightRef.current = false;
-	}, []);
+	React.useEffect(() => {
+		lastSeenMessageIdRef.current = null;
+		markSeenInFlightRef.current = false;
+	}, [realConversationId]);
📝 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
React.useEffect(() => {
lastSeenMessageIdRef.current = null;
markSeenInFlightRef.current = false;
}, [realConversationId]);
}, []);
// packages/react/src/support/pages/conversation.tsx
// … somewhere above you have something like:
// const realConversationId = …;
React.useEffect(() => {
lastSeenMessageIdRef.current = null;
markSeenInFlightRef.current = false;
}, [realConversationId]);
🤖 Prompt for AI Agents
In packages/react/src/support/pages/conversation.tsx around lines 153-157, the
useEffect currently only runs on mount so lastSeenMessageIdRef and
markSeenInFlightRef can leak across conversation switches; update the effect to
depend on the active conversation identifier (e.g., conversation?.id or
conversationId prop) instead of an empty array so the refs are reset whenever
the conversation changes, keeping the same reset logic inside the effect.

@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: 6

🧹 Nitpick comments (3)
packages/react/src/support/components/support-content.tsx (1)

12-24: Expose style (and optionally asChild) to meet headless component contract.

Per package guidelines, components should accept className and style (and often asChild). Consider this minimal tweak.

As per coding guidelines.

 type SupportContentProps = {
   className?: string;
+  style?: React.CSSProperties;
   position?: "top" | "bottom";
   align?: "right" | "left";
   mode?: "floating" | "responsive";
 };
 
 export const SupportContent: React.FC<SupportContentProps> = ({
   className,
+  style,
   position = "bottom",
   align = "right",
   mode = "floating",
 }) => {
@@
-    <motion.div
+    <motion.div
       animate={{ opacity: 1 }}
       className={containerClasses}
+      style={style}
       initial={{ opacity: 0 }}
packages/react/src/hooks/utils/use-client-query.ts (2)

44-51: Harden key hashing against circular/non‑serializable values.

JSON.stringify will throw on circular structures; add a safe fallback to prevent runtime errors during re‑keys.

-function toHash(key: readonly unknown[] | string): string {
+function toHash(key: readonly unknown[] | string): string {
   if (typeof key === "string") {
     return key;
   }
-
-  return JSON.stringify(key);
+  try {
+    return JSON.stringify(key);
+  } catch {
+    // Fallback: best‑effort stable-ish string
+    return Array.isArray(key) ? key.map((v) => String(v)).join("|") : String(key);
+  }
 }

111-160: De‑dupe overlapping fetches to avoid interval/focus storms.

Multiple triggers can start concurrent requests; only latest wins but the rest still hit the backend. Track an in‑flight promise and return it.

 export function useClientQuery<TData, TArgs = void>(
@@
   const latestArgsRef = useRef<TArgs | undefined>(initialArgs);
   const fetchIdRef = useRef(0);
+  const inFlightRef = useRef<Promise<TData> | null>(null);
@@
   const execute = useCallback(
     async (args?: TArgs, ignoreEnabled = false): Promise<TData | undefined> => {
       if (!(client && (enabled || ignoreEnabled))) {
         return stateRef.current.data;
       }
 
+      // Return existing request if one is in-flight
+      if (inFlightRef.current) {
+        return inFlightRef.current.then(
+          (v) => v,
+          () => stateRef.current.data
+        );
+      }
+
       const fetchId = fetchIdRef.current + 1;
       fetchIdRef.current = fetchId;
       latestArgsRef.current = args;
@@
       try {
-        const result = await queryFn(client, args);
+        const promise = queryFn(client, args);
+        inFlightRef.current = promise;
+        const result = await promise;
 
         if (!isMountedRef.current || fetchId !== fetchIdRef.current) {
           return stateRef.current.data;
         }
 
         setState({ data: result, error: null, status: "success" });
         hasFetchedRef.current = true;
 
         return result;
       } catch (error: unknown) {
         if (!isMountedRef.current || fetchId !== fetchIdRef.current) {
           return stateRef.current.data;
         }
 
         const normalized = toError(error);
         setState((current) => ({
           data: current.data,
           error: normalized,
           status: "error",
         }));
 
         throw normalized;
+      } finally {
+        inFlightRef.current = null;
       }
     },
     [client, enabled, queryFn]
   );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20aec58 and d061151.

📒 Files selected for processing (21)
  • packages/next/src/hooks/use-rest-client.ts (1 hunks)
  • packages/react/src/hooks/index.ts (1 hunks)
  • packages/react/src/hooks/store/use-conversations-store.ts (1 hunks)
  • packages/react/src/hooks/store/use-store-selector.ts (1 hunks)
  • packages/react/src/hooks/store/use-website-store.ts (1 hunks)
  • packages/react/src/hooks/use-conversation-messages.ts (1 hunks)
  • packages/react/src/hooks/use-conversations.ts (1 hunks)
  • packages/react/src/hooks/utils/use-client-query.ts (1 hunks)
  • packages/react/src/hooks/utils/use-default-messages.ts (1 hunks)
  • packages/react/src/hooks/utils/use-grouped-messages.ts (0 hunks)
  • packages/react/src/hooks/utils/use-realtime-support.ts (1 hunks)
  • packages/react/src/index.ts (1 hunks)
  • packages/react/src/provider.tsx (2 hunks)
  • packages/react/src/realtime/index.ts (1 hunks)
  • packages/react/src/realtime/support-provider.tsx (2 hunks)
  • packages/react/src/support/components/support-content.tsx (1 hunks)
  • packages/react/src/support/hooks/index.ts (1 hunks)
  • packages/react/src/support/hooks/use-conversation.ts (1 hunks)
  • packages/react/src/support/hooks/use-messages.ts (0 hunks)
  • packages/react/src/support/hooks/use-website.ts (1 hunks)
  • packages/react/src/support/pages/conversation.tsx (4 hunks)
💤 Files with no reviewable changes (2)
  • packages/react/src/hooks/utils/use-grouped-messages.ts
  • packages/react/src/support/hooks/use-messages.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/next/src/hooks/use-rest-client.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react/src/support/hooks/use-website.ts
🧰 Additional context used
📓 Path-based instructions (13)
packages/react/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Develop headless React components (including and primitives) under packages/react/src/

Files:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/realtime/support-provider.tsx
**/*.{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/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.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/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.ts
**/*.{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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/realtime/support-provider.tsx
**/*.{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/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.ts
**/*.{js,tsx,ts,jsx}

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

Use arrow functions instead of function expressions

Files:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.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/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.ts
**/*.{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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/realtime/support-provider.tsx
**/*.{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/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.ts
packages/react/**/*.{ts,tsx}

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

packages/react/**/*.{ts,tsx}: Components must render with no built-in styles (headless, unstyled)
Components must accept className, style, and asChild props to enable external styling/control
Do not use emotion, styled-components, or style-prop APIs; styling is via Tailwind/CSS through className/style
Use useRenderElement to create elements and encapsulate render logic (supporting intrinsic tags and render prop overrides)
Components with internal state must be controllable via props (e.g., value/defaultValue/onChange or isOpen/onOpenChange)
Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child
May reference Base UI logic internally, but never expose Base UI or MUI APIs; do not leak getRootProps/getInputProps; prefer useRenderElement
Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components
Provide a render prop API: render?: (props, state) => ReactElement, and allow className to be string or (state) => string
Support developer-owned rendering: components should not impose visual structure and should allow parent-driven markup via render prop/asChild

Files:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.ts
packages/**

📄 CodeRabbit inference engine (AGENTS.md)

Put shared contracts and utilities in packages/* (e.g., core, types, transactional)

Files:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/provider.tsx
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.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/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/utils/use-default-messages.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/hooks/store/use-conversations-store.ts
  • packages/react/src/hooks/store/use-store-selector.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/utils/use-realtime-support.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/realtime/index.ts
  • packages/react/src/index.ts
🧠 Learnings (9)
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to apps/web/src/hooks/use*.{ts,tsx} : Name React hooks with a use* prefix and place them in apps/web/src/hooks

Applied to files:

  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/index.ts
📚 Learning: 2025-09-17T09:00:28.751Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T09:00:28.751Z
Learning: Applies to packages/react/src/**/*.{ts,tsx} : Develop headless React components (including <Support /> and primitives) under packages/react/src/

Applied to files:

  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/index.ts
📚 Learning: 2025-09-17T16:59:53.676Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/react-package-rules.mdc:0-0
Timestamp: 2025-09-17T16:59:53.676Z
Learning: Applies to packages/react/**/*.{ts,tsx} : Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child

Applied to files:

  • packages/react/src/support/components/support-content.tsx
  • packages/react/src/hooks/index.ts
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.{ts,tsx} : Manage shared component state via React Context

Applied to files:

  • packages/react/src/realtime/support-provider.tsx
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{jsx,tsx} : Ensure all dependencies are correctly specified in React hooks

Applied to files:

  • packages/react/src/hooks/index.ts
📚 Learning: 2025-09-17T16:59:53.676Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/react-package-rules.mdc:0-0
Timestamp: 2025-09-17T16:59:53.676Z
Learning: Applies to packages/react/**/*.{ts,tsx} : Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components

Applied to files:

  • packages/react/src/hooks/index.ts
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to apps/api/src/ws/**/*.{ts,tsx} : Place WebSocket handlers under apps/api/src/ws

Applied to files:

  • packages/react/src/hooks/utils/use-realtime-support.ts
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to **/*.{ts,tsx} : Prefer explicit return types on exported APIs in TypeScript

Applied to files:

  • packages/react/src/index.ts
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.{ts,tsx} : Export type definitions for the public API

Applied to files:

  • packages/react/src/index.ts
🧬 Code graph analysis (11)
packages/react/src/support/pages/conversation.tsx (2)
packages/react/src/hooks/use-conversation-messages.ts (1)
  • useConversationMessages (46-127)
packages/react/src/index.ts (1)
  • useConversationMessages (6-6)
packages/react/src/hooks/use-conversations.ts (5)
packages/types/src/api/conversation.ts (2)
  • ListConversationsResponse (109-111)
  • ListConversationsRequest (90-92)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
packages/react/src/provider.tsx (1)
  • useSupport (226-243)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (52-254)
packages/react/src/hooks/store/use-website-store.ts (2)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (52-254)
packages/react/src/provider.tsx (1)
packages/react/src/hooks/store/use-website-store.ts (1)
  • useWebsiteStore (67-112)
packages/react/src/support/hooks/use-conversation.ts (5)
packages/core/src/store/conversations-store.ts (1)
  • ConversationsState (7-11)
packages/types/src/api/conversation.ts (2)
  • GetConversationResponse (135-137)
  • GetConversationRequest (123-125)
packages/react/src/provider.tsx (1)
  • useSupport (226-243)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (52-254)
packages/react/src/hooks/store/use-conversations-store.ts (2)
packages/react/src/provider.tsx (1)
  • useSupport (226-243)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/realtime/support-provider.tsx (3)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/registry.ts (1)
  • handlers (10-13)
packages/react/src/realtime/index.ts (1)
  • RealtimeEventHandlersMap (26-28)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts (1)
  • RealtimeEventHandlersMap (28-29)
packages/react/src/hooks/store/use-store-selector.ts (1)
packages/core/src/store/create-store.ts (1)
  • subscribe (50-55)
packages/react/src/hooks/use-conversation-messages.ts (4)
packages/core/src/store/messages-store.ts (2)
  • ConversationMessagesState (6-10)
  • MessagesState (12-14)
packages/react/src/provider.tsx (1)
  • useSupport (226-243)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (52-254)
packages/react/src/hooks/utils/use-client-query.ts (1)
packages/react/src/index.ts (1)
  • useClientQuery (21-21)
packages/react/src/realtime/index.ts (2)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts (4)
  • RealtimeEventHandlerContext (19-20)
  • RealtimeEventHandlerParams (22-23)
  • RealtimeEventHandler (25-26)
  • RealtimeEventHandlersMap (28-29)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/registry.ts (2)
  • createRealtimeEventDispatcher (15-22)
  • handlers (10-13)
⏰ 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 (8)
packages/react/src/support/components/support-content.tsx (1)

5-5: Path realignment LGTM.

The updated utils path for useMultimodalInput matches the hook reorg.

packages/react/src/realtime/index.ts (1)

175-180: Subscription effect is clean and minimal.

useRealtimeEvents correctly subscribes/unsubscribes using the memoized dispatcher.

packages/react/src/hooks/utils/use-default-messages.ts (1)

1-1: Core’s nanoid dependency is already declared
The core package.json lists "nanoid": "^5.1.5" under dependencies (packages/core/package.json:32), so the “Cannot find package 'nanoid'” error points to an install or workspace‐linking issue rather than a missing dep. Reinstall your node_modules or verify your monorepo setup.

Likely an incorrect or invalid review comment.

packages/react/src/hooks/utils/use-realtime-support.ts (1)

3-3: Import path and export verified
useWebSocket is correctly defined and exported in packages/react/src/support/context/websocket.tsx, and no circular imports were introduced by the move.

packages/react/src/hooks/store/use-website-store.ts (1)

74-83: Fix the non-serializable query key before this ships

useClientQuery still hashes keys via JSON.stringify (see toHash in use-client-query.ts). Passing the live CossistantClient instance into queryKey will either throw (TypeError: Converting circular structure to JSON) or collapse every client to the same cache bucket ({}), so subsequent renders will reuse/evict the wrong website data. Please replace the object with a stable scalar identifier (e.g., derive a string from the client’s public key / API origin, or expose a dedicated client.getIdentityKey() and use that here) before merging.

packages/react/src/support/pages/conversation.tsx (1)

117-120: Reset seen refs when conversation switches

This effect still only runs once, so the seen-tracking refs leak between different conversations. The stale IDs break the “mark as seen” flow after navigating. Please restore the dependency on realConversationId.

-	}, []);
+	}, [realConversationId]);
packages/react/src/support/hooks/use-conversation.ts (1)

16-21: Match EMPTY_STORE.subscribe to the BasicStore signature.

The fallback store drops the listener parameter, so it can't be treated as a BasicStore. useStoreSelector passes a callback and relies on it firing; with the current signature we lose type safety and break updates whenever the real store is missing. Please accept the listener argument and return a noop unsubscribe.

 const EMPTY_STORE = {
 	getState: (): ConversationsState => EMPTY_STATE,
-	subscribe: () => () => {
+	subscribe: (_listener: () => void) => () => {
 		// noop
 	},
 };
packages/react/src/hooks/use-conversation-messages.ts (1)

20-25: Restore the EMPTY_MESSAGES_STORE.subscribe listener parameter.

This fallback store no longer satisfies the BasicStore shape—useStoreSelector passes a listener, but this stub ignores the argument, breaking type compatibility and preventing updates from ever propagating when the real store is absent. Please align the signature with the real store.

 const EMPTY_MESSAGES_STORE = {
 	getState: (): MessagesState => ({ conversations: {} }),
-	subscribe: () => () => {
+	subscribe: (_listener: () => void) => () => {
 		// noop
 	},
 };

Comment on lines +115 to +140
const { client } = useSupport();
let resolvedOptions: UseConversationsOptions;

if (isClientLike(clientOrOptions) || clientOrOptions === null) {
resolvedOptions = { ...DEFAULT_OPTIONS, ...(maybeOptions ?? {}) };
} else {
resolvedOptions = { ...DEFAULT_OPTIONS, ...(clientOrOptions ?? {}) };
}

const store = client?.conversationsStore ?? EMPTY_STORE;

const selection = useStoreSelector(
store,
(state): ConversationsSelection => ({
conversations: state.ids
.map((id) => state.byId[id])
.filter(
(
conversation
): conversation is ListConversationsResponse["conversations"][number] =>
Boolean(conversation)
),
pagination: state.pagination,
}),
areSelectionsEqual
);

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 | 🟠 Major

Honor the explicit client parameter

The overload useConversations(client, options) no longer works: clientOrOptions is ignored and we always pull client from context. Passing a client outside the provider now throws and even inside the provider the argument is a no-op. Wire the provided client through before falling back to context.

-	const { client } = useSupport();
-	let resolvedOptions: UseConversationsOptions;
-
-	if (isClientLike(clientOrOptions) || clientOrOptions === null) {
-		resolvedOptions = { ...DEFAULT_OPTIONS, ...(maybeOptions ?? {}) };
-	} else {
-		resolvedOptions = { ...DEFAULT_OPTIONS, ...(clientOrOptions ?? {}) };
-	}
-
-	const store = client?.conversationsStore ?? EMPTY_STORE;
+	const support = useSupport();
+	const contextClient = support.client;
+	const client =
+		isClientLike(clientOrOptions) || clientOrOptions === null
+			? clientOrOptions ?? contextClient
+			: contextClient;
+
+	let resolvedOptions: UseConversationsOptions;
+	if (isClientLike(clientOrOptions) || clientOrOptions === null) {
+		resolvedOptions = { ...DEFAULT_OPTIONS, ...(maybeOptions ?? {}) };
+	} else {
+		resolvedOptions = { ...DEFAULT_OPTIONS, ...(clientOrOptions ?? {}) };
+	}
+
+	const store = client?.conversationsStore ?? EMPTY_STORE;
📝 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
const { client } = useSupport();
let resolvedOptions: UseConversationsOptions;
if (isClientLike(clientOrOptions) || clientOrOptions === null) {
resolvedOptions = { ...DEFAULT_OPTIONS, ...(maybeOptions ?? {}) };
} else {
resolvedOptions = { ...DEFAULT_OPTIONS, ...(clientOrOptions ?? {}) };
}
const store = client?.conversationsStore ?? EMPTY_STORE;
const selection = useStoreSelector(
store,
(state): ConversationsSelection => ({
conversations: state.ids
.map((id) => state.byId[id])
.filter(
(
conversation
): conversation is ListConversationsResponse["conversations"][number] =>
Boolean(conversation)
),
pagination: state.pagination,
}),
areSelectionsEqual
);
const support = useSupport();
const contextClient = support.client;
const client =
isClientLike(clientOrOptions) || clientOrOptions === null
? clientOrOptions ?? contextClient
: contextClient;
let resolvedOptions: UseConversationsOptions;
if (isClientLike(clientOrOptions) || clientOrOptions === null) {
resolvedOptions = { ...DEFAULT_OPTIONS, ...(maybeOptions ?? {}) };
} else {
resolvedOptions = { ...DEFAULT_OPTIONS, ...(clientOrOptions ?? {}) };
}
const store = client?.conversationsStore ?? EMPTY_STORE;
const selection = useStoreSelector(
store,
(state): ConversationsSelection => ({
conversations: state.ids
.map((id) => state.byId[id])
.filter(
(
conversation
): conversation is ListConversationsResponse["conversations"][number] =>
Boolean(conversation)
),
pagination: state.pagination,
}),
areSelectionsEqual
);
🤖 Prompt for AI Agents
In packages/react/src/hooks/use-conversations.ts around lines 115 to 140, the
function always uses the client from context (const { client } = useSupport())
which ignores an explicitly passed client; update the logic to prefer a provided
client when clientOrOptions is client-like (use that client for store and any
client access) and only fall back to the context client otherwise; keep the
existing resolvedOptions logic but ensure store is derived from the chosen
client (providedClient ?? contextClient) so passing useConversations(client,
options) works again.

Comment on lines +17 to +35
type UseClientQueryOptions<TData, TArgs> = {
client: CossistantClient | null;
queryKey: readonly unknown[] | string;
queryFn: QueryFn<TData, TArgs>;
enabled?: boolean;
refetchInterval?: number | false;
refetchOnWindowFocus?: boolean;
refetchOnMount?: boolean;
initialData?: TData;
initialArgs?: TArgs;
};

type UseClientQueryResult<TData, TArgs> = {
data: TData | undefined;
error: Error | null;
isLoading: boolean;
refetch: (args?: TArgs) => Promise<TData | undefined>;
};

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.

🛠️ Refactor suggestion | 🟠 Major

Export hook option/result types for public API re-exports.

PR description mentions re‑exporting these types; they’re currently internal. Exporting aligns with our TS API guidelines and avoids broken re-exports.

As per coding guidelines.

-type UseClientQueryOptions<TData, TArgs> = {
+export type UseClientQueryOptions<TData, TArgs> = {
   client: CossistantClient | null;
   queryKey: readonly unknown[] | string;
   queryFn: QueryFn<TData, TArgs>;
   enabled?: boolean;
   refetchInterval?: number | false;
   refetchOnWindowFocus?: boolean;
   refetchOnMount?: boolean;
   initialData?: TData;
   initialArgs?: TArgs;
 };
 
-type UseClientQueryResult<TData, TArgs> = {
+export type UseClientQueryResult<TData, TArgs> = {
   data: TData | undefined;
   error: Error | null;
   isLoading: boolean;
   refetch: (args?: TArgs) => Promise<TData | undefined>;
 };
📝 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
type UseClientQueryOptions<TData, TArgs> = {
client: CossistantClient | null;
queryKey: readonly unknown[] | string;
queryFn: QueryFn<TData, TArgs>;
enabled?: boolean;
refetchInterval?: number | false;
refetchOnWindowFocus?: boolean;
refetchOnMount?: boolean;
initialData?: TData;
initialArgs?: TArgs;
};
type UseClientQueryResult<TData, TArgs> = {
data: TData | undefined;
error: Error | null;
isLoading: boolean;
refetch: (args?: TArgs) => Promise<TData | undefined>;
};
export type UseClientQueryOptions<TData, TArgs> = {
client: CossistantClient | null;
queryKey: readonly unknown[] | string;
queryFn: QueryFn<TData, TArgs>;
enabled?: boolean;
refetchInterval?: number | false;
refetchOnWindowFocus?: boolean;
refetchOnMount?: boolean;
initialData?: TData;
initialArgs?: TArgs;
};
export type UseClientQueryResult<TData, TArgs> = {
data: TData | undefined;
error: Error | null;
isLoading: boolean;
refetch: (args?: TArgs) => Promise<TData | undefined>;
};
🤖 Prompt for AI Agents
In packages/react/src/hooks/utils/use-client-query.ts around lines 17 to 35, the
hook option/result types are declared but not exported; export both
UseClientQueryOptions and UseClientQueryResult so they become part of the public
API and can be re-exported by the package. Update the type declarations to be
exported (export type ...) and ensure any related imports/exports in the package
index re-export these types if required by the PR.

Comment on lines +9 to 10
export type RealtimeEventHandlerContext<TContext = EmptyObject> = {} & TContext;

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 | 🔴 Critical

🧩 Analysis chain

Context type change—verify downstreams no longer depend on queryClient.

Since RealtimeEventHandlerContext dropped QueryClient, confirm all consumers (apps/web and others) pass the new context shape and don’t read queryClient.

Run:


🏁 Script executed:

#!/bin/bash
rg -nP '\bRealtimeEventHandlerContext<|context\.\s*queryClient\b' --glob '!**/node_modules/**'

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Search for usages of the updated context type
rg -nP '\bRealtimeEventHandlerContext\b' --glob '*.ts' --glob '*.tsx'
# Search for any remaining direct access to queryClient on the context object
rg -nP 'context\.\s*queryClient\b' --glob '*.ts' --glob '*.tsx'

Length of output: 1399


Remove direct context.queryClient references

  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts lines 189 and 206 still call context.queryClient; refactor these handlers to align with the new RealtimeEventHandlerContext (e.g. inject QueryClient via TContext or update to the revised API).
🤖 Prompt for AI Agents
In packages/react/src/realtime/index.ts around lines 9 to 10, the
RealtimeEventHandlerContext type was simplified to "{} & TContext" which removed
any built-in queryClient; update the conversation-seen handlers at
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
(lines ~189 and ~206) to stop referencing context.queryClient directly: either
extend the RealtimeEventHandlerContext with a QueryClient in the TContext when
wiring handlers (inject QueryClient into TContext) or change those handler
functions to accept QueryClient via parameters or a factory closure; implement
the chosen approach consistently and update types/signatures so handlers access
the QueryClient from the injected TContext (or param) instead of
context.queryClient.

Comment thread packages/react/src/realtime/index.ts Outdated
Comment on lines +57 to +74
for (const handler of eventHandlers) {
try {
handler({
event,
context,
});
} catch (error) {
if (onError) {
onError(error, event.type);
} else {
console.error("[Realtime] Event handler failed", {
error,
eventType: event.type,
});
}
}
}
};

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 | 🟠 Major

Async handler errors are not caught; remove console usage and route through onError.

try/catch won’t catch rejections from async handlers; current fallback logs via console.error (forbidden by guidelines). Wrap handler calls in Promise.resolve(...).catch(...) and rely solely on onError.

As per coding guidelines.

-    for (const handler of eventHandlers) {
-      try {
-        handler({
-          event,
-          context,
-        });
-      } catch (error) {
-        if (onError) {
-          onError(error, event.type);
-        } else {
-          console.error("[Realtime] Event handler failed", {
-            error,
-            eventType: event.type,
-          });
-        }
-      }
-    }
+    for (const handler of eventHandlers) {
+      // Normalize sync/async handlers and catch rejections
+      void Promise.resolve(
+        handler({
+          event,
+          context,
+        })
+      ).catch((error) => {
+        onError?.(error, event.type);
+      });
+    }
📝 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
for (const handler of eventHandlers) {
try {
handler({
event,
context,
});
} catch (error) {
if (onError) {
onError(error, event.type);
} else {
console.error("[Realtime] Event handler failed", {
error,
eventType: event.type,
});
}
}
}
};
for (const handler of eventHandlers) {
// Normalize sync/async handlers and catch rejections
void Promise.resolve(
handler({
event,
context,
})
).catch((error) => {
onError?.(error, event.type);
});
}
🤖 Prompt for AI Agents
In packages/react/src/realtime/index.ts around lines 57 to 74, the current
try/catch won't catch rejections from async event handlers and falls back to
console.error (forbidden). Replace the synchronous try/catch with
Promise.resolve(handler({ event, context })).catch(err => { if (onError)
onError(err, event.type); }); and remove the console.error fallback so all async
and sync handler errors are routed exclusively through onError to avoid
unhandled rejections and forbidden console usage.

Comment thread packages/react/src/realtime/index.ts Outdated
Comment on lines +121 to +133
try {
message = mapEventToMessage(params);
} catch (error) {
if (onTransformError) {
onTransformError(error, params);
} else {
console.error("[Realtime] Failed to transform MESSAGE_CREATED event", {
error,
eventType: params.event.type,
});
}
return;
}

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 | 🟠 Major

Same issue in transform error path; don’t log to console.

Route transform failures through onTransformError only; drop console logging.

As per coding guidelines.

-    } catch (error) {
-      if (onTransformError) {
-        onTransformError(error, params);
-      } else {
-        console.error("[Realtime] Failed to transform MESSAGE_CREATED event", {
-          error,
-          eventType: params.event.type,
-        });
-      }
-      return;
-    }
+    } catch (error) {
+      onTransformError?.(error, params);
+      return;
+    }
📝 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
try {
message = mapEventToMessage(params);
} catch (error) {
if (onTransformError) {
onTransformError(error, params);
} else {
console.error("[Realtime] Failed to transform MESSAGE_CREATED event", {
error,
eventType: params.event.type,
});
}
return;
}
try {
message = mapEventToMessage(params);
} catch (error) {
onTransformError?.(error, params);
return;
}
🤖 Prompt for AI Agents
In packages/react/src/realtime/index.ts around lines 121 to 133, the transform
error path currently falls back to console.error when onTransformError is not
provided; change this so transform failures are only routed through
onTransformError and remove the console logging. Modify the catch block to call
onTransformError(error, params) if present, and otherwise silently return (no
console.error), ensuring no direct console logging occurs in this path per
guidelines.

Comment on lines +58 to 59
const messagesQuery = useConversationMessages(conversationId);

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 | 🟠 Major

Prevent getConversationMessages calls for the pending conversation placeholder

conversationId is still the sentinel PENDING_CONVERSATION_ID until the server conversation exists, but the new hook fires immediately and invokes getConversationMessages with that fake id. The request will 404 and leave the hook stuck in an error state. Gate the query on hasRealConversation so we only fetch when the real id is available.

-	const messagesQuery = useConversationMessages(conversationId);
+	const messagesQuery = useConversationMessages(conversationId, {
+		enabled: hasRealConversation,
+	});
📝 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
const messagesQuery = useConversationMessages(conversationId);
─ const messagesQuery = useConversationMessages(conversationId);
const messagesQuery = useConversationMessages(conversationId, {
enabled: hasRealConversation,
});
🤖 Prompt for AI Agents
In packages/react/src/support/pages/conversation.tsx around lines 58-59, the
hook useConversationMessages is being called with the sentinel
PENDING_CONVERSATION_ID which triggers a 404; change the call so it is gated by
hasRealConversation — e.g. call useConversationMessages(hasRealConversation ?
conversationId : undefined, { enabled: hasRealConversation }) or pass an options
object with enabled: hasRealConversation so the query only runs once a real
conversationId exists.

…minate-react-query-usage

refactor(react): simplify hooks around support client context

@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)
packages/react/src/support/pages/conversation.tsx (2)

110-112: Surface send errors in the UI

Currently, send failures are only logged; include sendMessage.error so the input can display it.

-    const actualError = error || messagesQuery.error;
+    const actualError = error || messagesQuery.error || sendMessage.error;

70-95: Files passed to mutate are ignored downstream

useSendMessage doesn’t accept/forward files; attachments won’t be sent. Either remove files here or plumb them through the hook and payload builder.

Proposed minimal plumbing (outside this file):

  • packages/react/src/hooks/use-send-message.ts
-       async (payload: SendMessageOptions): Promise<SendMessageResult | null> => {
+       async (payload: SendMessageOptions): Promise<SendMessageResult | null> => {
            const {
                conversationId: providedConversationId,
                message,
+               files,
                defaultMessages = [],
                visitorId,
                onSuccess,
                onError,
            } = payload;
@@
-           const messagePayload = buildMessagePayload(
-               message,
-               conversationId,
-               visitorId ?? null
-           );
+           const messagePayload = buildMessagePayload(
+               message,
+               conversationId,
+               visitorId ?? null,
+               files ?? []
+           );
  • Ensure SendMessageOptions includes files?: File[] and update buildMessagePayload signature to accept and embed files.
🧹 Nitpick comments (5)
packages/react/src/hooks/store/use-website-store.ts (2)

21-24: Expose enabled option for parity with other hooks

Add enabled?: boolean and wire it to useClientQuery (defaults to true). Improves control/consistency with hooks like useConversationMessages.

 export type UseWebsiteStoreOptions = {
+       enabled?: boolean;
        refetchInterval?: number | false;
        refetchOnWindowFocus?: boolean;
 };

Also applies to: 50-50


39-44: Consider reading client from context for API consistency

Other hooks (useConversation, useConversationMessages) source the client from useSupport(). Making client optional here (fallback to context) would unify the API surface.

packages/react/src/support/pages/conversation.tsx (1)

91-93: Remove console usage; route errors to UI/logging infra

Project guideline forbids console. Drop logs and rely on actualError (above) or a logger service.

As per coding guidelines

-            onError: (_error) => {
-                console.error("Failed to send message:", _error);
-            },
+            onError: () => {},
-                console.error("Failed to mark conversation as seen:", markSeenError);
+                // no-op (error surfaced via store/hooks if needed)

Also applies to: 149-149

packages/react/src/hooks/use-conversation.ts (1)

1-8: Stabilize refetch with useCallback

Not mandatory, but keeps identity stable for consumers.

+import { useCallback } from "react";
@@
-        const refetch = (args?: GetConversationRequest) => {
+        const refetch = useCallback((args?: GetConversationRequest) => {
                 if (!conversationId) {
                         return Promise.resolve(undefined);
                 }
 
                 return queryRefetch({
                         conversationId,
                         ...args,
                 });
-        };
+        }, [conversationId, queryRefetch]);

Also applies to: 62-71

packages/react/src/hooks/utils/use-rest-client.ts (1)

12-17: Documented error handling doesn't match implementation

The JSDoc promises we surface configuration issues via the error field, but the hook throws instead and always returns { error: null }. Either wire the catch branch to populate error (and stop throwing), or update the return type/docs to drop error.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d061151 and 9996a5b.

📒 Files selected for processing (17)
  • packages/react/src/hooks/index.ts (1 hunks)
  • packages/react/src/hooks/store/use-website-store.ts (1 hunks)
  • packages/react/src/hooks/use-conversation-messages.ts (1 hunks)
  • packages/react/src/hooks/use-conversation.ts (1 hunks)
  • packages/react/src/hooks/use-conversations.ts (1 hunks)
  • packages/react/src/hooks/use-create-conversation.ts (1 hunks)
  • packages/react/src/hooks/use-send-message.ts (1 hunks)
  • packages/react/src/hooks/use-visitor.ts (1 hunks)
  • packages/react/src/hooks/utils/use-client-query.ts (1 hunks)
  • packages/react/src/hooks/utils/use-rest-client.ts (1 hunks)
  • packages/react/src/index.ts (1 hunks)
  • packages/react/src/provider.tsx (3 hunks)
  • packages/react/src/support/hooks/index.ts (1 hunks)
  • packages/react/src/support/hooks/use-conversation.ts (1 hunks)
  • packages/react/src/support/hooks/use-create-conversation.ts (1 hunks)
  • packages/react/src/support/hooks/use-send-message.ts (1 hunks)
  • packages/react/src/support/pages/conversation.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react/src/hooks/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
🧰 Additional context used
📓 Path-based instructions (13)
packages/react/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Develop headless React components (including and primitives) under packages/react/src/

Files:

  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.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/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.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/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.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/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.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/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.ts
**/*.{js,tsx,ts,jsx}

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

Use arrow functions instead of function expressions

Files:

  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.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/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.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/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.ts
packages/react/**/*.{ts,tsx}

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

packages/react/**/*.{ts,tsx}: Components must render with no built-in styles (headless, unstyled)
Components must accept className, style, and asChild props to enable external styling/control
Do not use emotion, styled-components, or style-prop APIs; styling is via Tailwind/CSS through className/style
Use useRenderElement to create elements and encapsulate render logic (supporting intrinsic tags and render prop overrides)
Components with internal state must be controllable via props (e.g., value/defaultValue/onChange or isOpen/onOpenChange)
Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child
May reference Base UI logic internally, but never expose Base UI or MUI APIs; do not leak getRootProps/getInputProps; prefer useRenderElement
Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components
Provide a render prop API: render?: (props, state) => ReactElement, and allow className to be string or (state) => string
Support developer-owned rendering: components should not impose visual structure and should allow parent-driven markup via render prop/asChild

Files:

  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.ts
packages/**

📄 CodeRabbit inference engine (AGENTS.md)

Put shared contracts and utilities in packages/* (e.g., core, types, transactional)

Files:

  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-visitor.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/index.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/utils/use-client-query.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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/provider.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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/provider.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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/provider.tsx
🧠 Learnings (5)
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to apps/web/src/hooks/use*.{ts,tsx} : Name React hooks with a use* prefix and place them in apps/web/src/hooks

Applied to files:

  • packages/react/src/support/hooks/index.ts
  • packages/react/src/support/hooks/use-send-message.ts
📚 Learning: 2025-09-17T09:00:28.751Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T09:00:28.751Z
Learning: Applies to packages/react/src/**/*.{ts,tsx} : Develop headless React components (including <Support /> and primitives) under packages/react/src/

Applied to files:

  • packages/react/src/index.ts
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to **/*.{ts,tsx} : Prefer explicit return types on exported APIs in TypeScript

Applied to files:

  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.{ts,tsx} : Export type definitions for the public API

Applied to files:

  • packages/react/src/hooks/utils/use-client-query.ts
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{ts,tsx} : Use export type for types

Applied to files:

  • packages/react/src/hooks/utils/use-client-query.ts
🧬 Code graph analysis (10)
packages/react/src/hooks/use-conversation.ts (4)
packages/types/src/api/conversation.ts (2)
  • GetConversationResponse (135-137)
  • GetConversationRequest (123-125)
packages/react/src/provider.tsx (1)
  • useSupport (221-238)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (38-219)
packages/react/src/hooks/store/use-website-store.ts (2)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (38-219)
packages/react/src/hooks/use-visitor.ts (2)
packages/react/src/provider.tsx (1)
  • useSupport (221-238)
packages/types/src/api/visitor.ts (2)
  • VisitorMetadata (12-12)
  • VisitorResponse (353-353)
packages/react/src/support/pages/conversation.tsx (2)
packages/react/src/hooks/use-conversation-messages.ts (1)
  • useConversationMessages (36-112)
packages/react/src/hooks/use-send-message.ts (1)
  • useSendMessage (75-183)
packages/react/src/hooks/use-send-message.ts (4)
packages/types/src/api/conversation.ts (1)
  • CreateConversationResponseBody (48-50)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
packages/types/src/enums.ts (4)
  • MessageType (1-5)
  • MessageType (7-7)
  • MessageVisibility (33-36)
  • MessageVisibility (74-75)
packages/react/src/provider.tsx (1)
  • useSupport (221-238)
packages/react/src/hooks/use-conversation-messages.ts (4)
packages/core/src/store/messages-store.ts (1)
  • ConversationMessagesState (6-10)
packages/react/src/provider.tsx (1)
  • useSupport (221-238)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (38-219)
packages/react/src/provider.tsx (3)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
packages/react/src/hooks/utils/use-rest-client.ts (1)
  • useClient (18-51)
packages/react/src/hooks/store/use-website-store.ts (1)
  • useWebsiteStore (38-79)
packages/react/src/hooks/use-conversations.ts (3)
packages/react/src/provider.tsx (1)
  • useSupport (221-238)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (38-219)
packages/react/src/hooks/use-create-conversation.ts (3)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
packages/types/src/api/conversation.ts (2)
  • CreateConversationResponseBody (48-50)
  • CreateConversationRequestBody (35-37)
packages/react/src/provider.tsx (1)
  • useSupport (221-238)
packages/react/src/hooks/utils/use-rest-client.ts (2)
packages/react/src/index.ts (1)
  • useClient (27-27)
packages/next/src/hooks/use-rest-client.ts (1)
  • useClient (3-3)
⏰ 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 (3)
packages/react/src/hooks/utils/use-client-query.ts (1)

9-26: Export the public hook types to avoid build breakage.

UseClientQueryOptions and UseClientQueryResult are consumed via downstream re-exports (per the PR summary), but they aren’t actually exported here anymore. That makes the package emit Module has no exported member errors during type generation/build, so the new hook surface can’t be used. Please restore the exports.

-type UseClientQueryOptions<TData, TArgs> = {
+export type UseClientQueryOptions<TData, TArgs> = {
@@
-};
- 
-type UseClientQueryResult<TData, TArgs> = {
+};
+
+export type UseClientQueryResult<TData, TArgs> = {
packages/react/src/support/pages/conversation.tsx (2)

117-121: Reset “seen” tracking when the conversation changes, not only on mount

Prevents leaking lastSeenMessageIdRef across conversation switches.

-    React.useEffect(() => {
+    React.useEffect(() => {
         lastSeenMessageIdRef.current = null;
         markSeenInFlightRef.current = false;
-    }, []);
+    }, [realConversationId]);

58-59: Gate messages query until a real conversation id exists

Avoid calling the API with the sentinel PENDING_CONVERSATION_ID, which 404s and poisons error state.

-    const messagesQuery = useConversationMessages(conversationId);
+    const messagesQuery = useConversationMessages(conversationId, {
+        enabled: hasRealConversation,
+    });

Comment on lines +47 to +55
const query = useClientQuery<PublicWebsiteResponse, { force?: boolean }>({
client,
queryFn: (instance, params) => instance.fetchWebsite(params ?? {}),
enabled: true,
refetchInterval: options.refetchInterval ?? false,
refetchOnWindowFocus: options.refetchOnWindowFocus ?? true,
refetchOnMount: state.status === "idle",
initialData: state.website ?? undefined,
});

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 | 🔴 Critical

Fix type mismatch and refresh return; return website, not the response envelope

useClientQuery is typed as PublicWebsiteResponse but initialData is state.website and refresh() returns a website. Align TData to the website shape and extract response.website from fetchWebsite. Also prefer the store variable over client.websiteStore in refresh.

-        const query = useClientQuery<PublicWebsiteResponse, { force?: boolean }>({
-                client,
-                queryFn: (instance, params) => instance.fetchWebsite(params ?? {}),
-                enabled: true,
-                refetchInterval: options.refetchInterval ?? false,
-                refetchOnWindowFocus: options.refetchOnWindowFocus ?? true,
-                refetchOnMount: state.status === "idle",
-                initialData: state.website ?? undefined,
-        });
+        const query = useClientQuery<WebsiteState["website"], { force?: boolean }>({
+                client,
+                queryFn: async (instance, params) => {
+                        const res = await instance.fetchWebsite(params ?? {});
+                        return res.website; // adapt if fetchWebsite already returns the website
+                },
+                enabled: options.enabled ?? true,
+                refetchInterval: options.refetchInterval ?? false,
+                refetchOnWindowFocus: options.refetchOnWindowFocus ?? true,
+                refetchOnMount: state.status === "idle",
+                initialData: state.website ?? undefined,
+        });
@@
-        const refresh = () => {
-                return query
-                        .refetch({ force: true })
-                        .then((result) => result ?? client.websiteStore.getState().website)
-                        .catch(() => client.websiteStore.getState().website)
-                        .then((website) => website ?? null);
-        };
+        const refresh = () => {
+                return query
+                        .refetch({ force: true })
+                        .then((website) => website ?? store.getState().website)
+                        .catch(() => store.getState().website)
+                        .then((website) => website ?? null);
+        };

Also applies to: 64-70

🤖 Prompt for AI Agents
In packages/react/src/hooks/store/use-website-store.ts around lines 47-55 (and
similarly adjust lines 64-70), the query is typed as PublicWebsiteResponse while
initialData and refresh expect the Website shape; change the query generic to
the Website type (not PublicWebsiteResponse), update queryFn to return
instance.fetchWebsite(...).website (extract the website from the response), and
set initialData to state.website as before; in the refresh function use the
local store variable (not client.websiteStore) to perform updates and return the
website object (not the response envelope). Ensure types/signatures are updated
accordingly so refresh() and the query return the Website type.

Comment on lines +100 to +101
const isInitialLoad = selection.messages.length === 0;
const isLoading = isInitialLoad ? queryLoading : false;

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 | 🟠 Major

Don’t mask ongoing loads once messages exist.

Right now isLoading is forced to false whenever the store already holds any messages. When fetchNextPage() or a manual refetch() runs, useClientQuery correctly flips queryLoading to true, but your wrapper hides that by zeroing it out. Consumers lose any signal that pagination or refresh is in flight, so spinners/loaders never render after the first page—this is a user-facing regression.

Let the hook surface the actual loading flag from useClientQuery (and, if needed, track an additional isInitialLoading separately), e.g.:

-	const isInitialLoad = selection.messages.length === 0;
-	const isLoading = isInitialLoad ? queryLoading : false;
+	const isLoading = queryLoading;
📝 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
const isInitialLoad = selection.messages.length === 0;
const isLoading = isInitialLoad ? queryLoading : false;
- const isInitialLoad = selection.messages.length === 0;
const isLoading = queryLoading;
🤖 Prompt for AI Agents
In packages/react/src/hooks/use-conversation-messages.ts around lines 100 to
101, the hook is masking ongoing loads by forcing isLoading to false when
messages already exist; change it to surface the real queryLoading from
useClientQuery (i.e., set isLoading = queryLoading) so pagination/refetch
loading states are preserved for consumers, and if you need to distinguish the
first load keep the existing isInitialLoad = selection.messages.length === 0 and
expose an additional isInitialLoading flag (isInitialLoading = isInitialLoad &&
queryLoading) rather than overriding queryLoading.

Comment on lines +29 to +31
const store = client.conversationsStore;

const conversation = useStoreSelector(store, (state) => {

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 | 🟠 Major

Add explicit store-availability guard

Throws a clear error instead of failing inside useStoreSelector when the store isn’t present.

-        const store = client.conversationsStore;
+        const store = client.conversationsStore ?? ((): typeof client.conversationsStore => {
+                throw new Error("Conversations store is not available on the client instance");
+        })();
📝 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
const store = client.conversationsStore;
const conversation = useStoreSelector(store, (state) => {
const store = client.conversationsStore ?? ((): typeof client.conversationsStore => {
throw new Error("Conversations store is not available on the client instance");
})();
const conversation = useStoreSelector(store, (state) => {
🤖 Prompt for AI Agents
In packages/react/src/hooks/use-conversation.ts around lines 29 to 31, the code
assumes client.conversationsStore exists and may fail inside useStoreSelector
when it's missing; add an explicit guard after obtaining const store =
client.conversationsStore and if store is null/undefined throw a clear,
descriptive error (e.g., "conversationsStore is not available on client") before
calling useStoreSelector so consumers get a helpful message instead of an
internal failure.

Comment thread packages/react/src/provider.tsx Outdated
Comment thread packages/react/src/provider.tsx Outdated
Comment on lines +98 to +101
if (website) {
// @ts-expect-error internal priming: safe in our library context
client.restClient?.setWebsiteContext?.(website.id, website.visitor?.id);
}

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.

🛠️ Refactor suggestion | 🟠 Major

Call the public client API instead of a private field

CossistantClient already exposes setWebsiteContext, so we can avoid poking the private restClient field and the accompanying @ts-expect-error. Relying on internals is brittle and will break as soon as that implementation detail changes.

-	if (website) {
-		// @ts-expect-error internal priming: safe in our library context
-		client.restClient?.setWebsiteContext?.(website.id, website.visitor?.id);
-	}
+	if (website) {
+		client.setWebsiteContext(website.id, website.visitor?.id);
+	}
📝 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 (website) {
// @ts-expect-error internal priming: safe in our library context
client.restClient?.setWebsiteContext?.(website.id, website.visitor?.id);
}
if (website) {
- // @ts-expect-error internal priming: safe in our library context
client.setWebsiteContext(website.id, website.visitor?.id);
}
🤖 Prompt for AI Agents
In packages/react/src/provider.tsx around lines 98 to 101, avoid accessing the
private restClient field and remove the @ts-expect-error: call the public API on
CossistantClient instead. Replace
client.restClient?.setWebsiteContext?.(website.id, website.visitor?.id) with a
call to client.setWebsiteContext(website.id, website.visitor?.id) (guarding with
an existence check if needed), and remove the @ts-expect-error comment so we
rely only on the public method.

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: 2

Caution

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

⚠️ Outside diff range comments (3)
packages/react/src/realtime/index.ts (1)

135-137: Don’t treat valid falsy messages as absent

if (!message) drops 0, "", or false. Check nullish only.

-    if (!message) {
+    if (message == null) {
       return;
     }
packages/react/src/support/pages/conversation.tsx (2)

69-76: Don’t pass null as conversationId to useSendMessage

conversationId is optional; pass it only when real to satisfy types and intent.

-    sendMessage.mutate({
-      conversationId: realConversationId,
+    sendMessage.mutate({
+      ...(hasRealConversation ? { conversationId } : {}),
       message: message.trim(),
       files,
       defaultMessages,
       visitorId: visitor?.id,

109-111: Surface sendMessage errors in the UI

Currently only parent/error or messagesQuery.error show. Include sendMessage.error so failed sends are visible.

- const actualError = error || messagesQuery.error;
+ const actualError = error || messagesQuery.error || sendMessage.error;
🧹 Nitpick comments (18)
packages/react/src/realtime/index.ts (1)

9-9: Redundant intersection; simplify context alias

{} & TContext ≡ TContext. Use a direct alias.

-export type RealtimeEventHandlerContext<TContext = EmptyObject> = {} & TContext;
+export type RealtimeEventHandlerContext<TContext = EmptyObject> = TContext;
packages/react/src/support/pages/conversation.tsx (1)

91-91: Avoid using console in production code

Route through an app logger or propagate via state/UI. If needed, guard for dev-only logging.

Example:

- console.error("Failed to send message:", _error);
+ // TODO: integrate with app logger or error boundary
+ if (process.env.NODE_ENV !== "production") {
+   // eslint-disable-next-line no-console
+   console.error("Failed to send message:", _error);
+ }

Apply similarly for the "mark conversation as seen" catch.

Also applies to: 148-148

packages/react/src/hooks/store/use-website-store.ts (3)

6-6: Remove unused PublicWebsiteResponse import

Type will be dropped after aligning query generics to Website shape.


67-69: Refine loading state to avoid spinner when website is already present

Treat idle as loading only when the store has no website yet.

- const isLoading =
-   query.isLoading || state.status === "loading" || state.status === "idle";
+ const isLoading =
+   query.isLoading ||
+   state.status === "loading" ||
+   (state.status === "idle" && !state.website);

70-76: Use the local store reference and return Website from refresh()

Avoid reaching back into client.websiteStore and keep return type consistent.

- const refresh = () => {
-   return query
-     .refetch({ force: true })
-     .then((result) => result ?? client.websiteStore.getState().website)
-     .catch(() => client.websiteStore.getState().website)
-     .then((website) => website ?? null);
- };
+ const refresh = () => {
+   return query
+     .refetch({ force: true })
+     .then((website) => website ?? store.getState().website)
+     .catch(() => store.getState().website)
+     .then((website) => website ?? null);
+ };
packages/react/src/hooks/use-conversation.ts (1)

42-60: Avoid unnecessary fetch when conversation is already in the store

useClientQuery will still fetch on mount if initialData is undefined. Gate via enabled: … && !conversation to skip the initial request when cache has the data.

-  } = useClientQuery<GetConversationResponse, GetConversationRequest>({
+  } = useClientQuery<GetConversationResponse, GetConversationRequest>({
     client,
     queryFn: (instance) => {
       if (!request) {
         throw new Error("Conversation ID is required");
       }
       return instance.getConversation(request);
     },
-    enabled: Boolean(conversationId && (options.enabled ?? true)),
+    enabled: Boolean(conversationId && (options.enabled ?? true) && !conversation),
     refetchInterval: options.refetchInterval ?? false,
     refetchOnWindowFocus: options.refetchOnWindowFocus ?? true,
     refetchOnMount: !conversation,
     initialArgs: request,
     dependencies: [conversationId ?? "null"],
   });
packages/react/src/hooks/utils/use-client-query.ts (3)

4-8: Also export QueryFn for better DX.

Consumers will often type their query functions; exporting this alias improves ergonomics.

Apply:

-type QueryFn<TData, TArgs> = (
+export type QueryFn<TData, TArgs> = (
   client: CossistantClient,
   args?: TArgs | undefined
 ) => Promise<TData>;

As per coding guidelines (export type for types).


131-139: Rename misleading variable: it refetches on every dependency change.

shouldFetchInitially is true on subsequent runs too. Rename for clarity.

Apply:

-    const shouldFetchInitially = hasMountedRef.current
-      ? true
-      : refetchOnMount || !hasFetchedRef.current;
+    const shouldFetch = hasMountedRef.current
+      ? true
+      : refetchOnMount || !hasFetchedRef.current;
@@
-    if (!shouldFetchInitially) {
+    if (!shouldFetch) {
       return;
     }

38-41: Add minimal JSDoc for the exported hook and types.

Public API should be documented (purpose, options, return).

Apply:

+/**
+ * Client-driven query hook with optional auto-refetch on interval/window focus.
+ * Avoids React Query keys; uses a provided queryFn and client instance.
+ */
 export function useClientQuery<TData, TArgs = void>(
   options: UseClientQueryOptions<TData, TArgs>
 ): UseClientQueryResult<TData, TArgs> {
+/** Options for useClientQuery. Pass a stable `dependencies` array to control refetch triggers. */
 export type UseClientQueryOptions<TData, TArgs> = {
+/** Result of useClientQuery. `refetch` bypasses `enabled`. */
 export type UseClientQueryResult<TData, TArgs> = {

As per coding guidelines.

Also applies to: 9-19, 21-26

packages/react/src/provider.tsx (4)

170-175: Add explicit return type to exported component.

Public APIs should have explicit return types.

Apply:

-export function SupportProvider({
+export function SupportProvider({
   children,
@@
-}: Props) {
+}: Props): JSX.Element {

As per coding guidelines (prefer explicit return types on exported APIs).


221-229: Add explicit return type to exported hook.

Annotate return type for useSupport.

Apply:

-export function useSupport() {
+export type UseSupportResult = CossistantContextValue & {
+  availableHumanAgents: NonNullable<CossistantContextValue["website"]>["availableHumanAgents"] | [];
+  availableAIAgents: NonNullable<CossistantContextValue["website"]>["availableAIAgents"] | [];
+  visitor: CossistantContextValue["website"] extends infer W
+    ? W extends { visitor?: infer V } ? V | undefined : undefined
+    : undefined;
+};
+
+export function useSupport(): UseSupportResult {

As per coding guidelines.


224-226: Tighten the error message.

Capitalize brand for consistency.

Apply:

-      "useSupport must be used within a cossistant SupportProvider"
+      "useSupport must be used within a Cossistant SupportProvider"

12-12: Dead prop: defaultOpen isn’t used.

Either wire it up or drop from Props to avoid API confusion.

Would you like me to wire it to a future UI state or remove it now?

packages/react/src/hooks/utils/use-rest-client.ts (1)

7-10: Return type still exposes error but hook now throws; simplify API.

error is always null and the JSDoc claims errors are returned, which is no longer true. Either restore non-throwing behavior or remove error from the API; recommend removal.

-export type UseClientResult = {
-	client: CossistantClient;
-	error: Error | null;
-};
+export type UseClientResult = {
+	client: CossistantClient;
+};
@@
-	return { client, error: null };
+	return { client };

Optionally update the JSDoc to note that the hook throws on misconfiguration.

Also applies to: 50-51

packages/react/src/hooks/use-conversation-messages.ts (1)

73-76: Guard queries when conversationId is empty.

If an empty string slips through, we'll issue a fetch with an invalid id. Gate enabled on a non-empty id.

-		enabled: options.enabled ?? true,
+		enabled: (options.enabled ?? true) && conversationId !== "",
packages/react/src/index.ts (1)

45-47: Prefer curated named exports over export * for stability.

Wildcard re-exports from "./provider" and "./realtime" make the root API surface volatile and can cause accidental symbol collisions as the modules evolve. Consider switching to explicit named exports (types via export type, values via export) for a stable, documented surface.

packages/react/src/hooks/index.ts (2)

7-12: Expose utils via named exports (avoid export *) to keep the public API intentional.

Wildcard re-exports can unintentionally grow the API surface and hinder type-only re-exports. Prefer named exports for utils.

Apply this diff:

- export * from "./utils/use-client-query";
- export * from "./utils/use-default-messages";
- export * from "./utils/use-grouped-messages";
- export * from "./utils/use-multimodal-input";
- export * from "./utils/use-realtime-support";
- export * from "./utils/use-rest-client";
+ export { useClientQuery } from "./utils/use-client-query";
+ export { useDefaultMessages } from "./utils/use-default-messages";
+ export { useGroupedMessages } from "./utils/use-grouped-messages";
+ export { useMultimodalInput } from "./utils/use-multimodal-input";
+ export { useRealtimeSupport } from "./utils/use-realtime-support";
+ export { useClient } from "./utils/use-rest-client";

1-5: Optional: mirror curated, type‑safe re‑exports for conversation hooks.

To align with root index style and “export type” guidance, consider switching these to explicit export type { … } + export { … } per module.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9996a5b and f0f711d.

📒 Files selected for processing (18)
  • packages/react/src/hooks/index.ts (1 hunks)
  • packages/react/src/hooks/store/use-website-store.ts (1 hunks)
  • packages/react/src/hooks/use-conversation-messages.ts (1 hunks)
  • packages/react/src/hooks/use-conversation.ts (1 hunks)
  • packages/react/src/hooks/use-conversations.ts (1 hunks)
  • packages/react/src/hooks/use-create-conversation.ts (1 hunks)
  • packages/react/src/hooks/use-send-message.ts (1 hunks)
  • packages/react/src/hooks/use-visitor.ts (0 hunks)
  • packages/react/src/hooks/utils/use-client-query.ts (1 hunks)
  • packages/react/src/hooks/utils/use-rest-client.ts (3 hunks)
  • packages/react/src/index.ts (1 hunks)
  • packages/react/src/provider.tsx (3 hunks)
  • packages/react/src/realtime/index.ts (1 hunks)
  • packages/react/src/support/hooks/index.ts (1 hunks)
  • packages/react/src/support/hooks/use-conversation.ts (1 hunks)
  • packages/react/src/support/hooks/use-create-conversation.ts (1 hunks)
  • packages/react/src/support/hooks/use-send-message.ts (1 hunks)
  • packages/react/src/support/pages/conversation.tsx (4 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/src/hooks/use-visitor.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/react/src/support/hooks/use-create-conversation.ts
  • packages/react/src/hooks/use-create-conversation.ts
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/hooks/use-send-message.ts
🧰 Additional context used
📓 Path-based instructions (13)
packages/react/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Develop headless React components (including and primitives) under packages/react/src/

Files:

  • packages/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.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/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.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/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.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/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.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/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.ts
**/*.{js,tsx,ts,jsx}

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

Use arrow functions instead of function expressions

Files:

  • packages/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.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/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.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/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.ts
packages/react/**/*.{ts,tsx}

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

packages/react/**/*.{ts,tsx}: Components must render with no built-in styles (headless, unstyled)
Components must accept className, style, and asChild props to enable external styling/control
Do not use emotion, styled-components, or style-prop APIs; styling is via Tailwind/CSS through className/style
Use useRenderElement to create elements and encapsulate render logic (supporting intrinsic tags and render prop overrides)
Components with internal state must be controllable via props (e.g., value/defaultValue/onChange or isOpen/onOpenChange)
Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child
May reference Base UI logic internally, but never expose Base UI or MUI APIs; do not leak getRootProps/getInputProps; prefer useRenderElement
Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components
Provide a render prop API: render?: (props, state) => ReactElement, and allow className to be string or (state) => string
Support developer-owned rendering: components should not impose visual structure and should allow parent-driven markup via render prop/asChild

Files:

  • packages/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.ts
packages/**

📄 CodeRabbit inference engine (AGENTS.md)

Put shared contracts and utilities in packages/* (e.g., core, types, transactional)

Files:

  • packages/react/src/realtime/index.ts
  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/use-conversation.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/index.ts
  • packages/react/src/hooks/utils/use-rest-client.ts
  • packages/react/src/hooks/store/use-website-store.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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/provider.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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/provider.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:

  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/provider.tsx
🧠 Learnings (8)
📚 Learning: 2025-09-17T09:06:53.619Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.619Z
Learning: Applies to **/*.{js,ts} : Don't use unnecessary catch clauses

Applied to files:

  • packages/react/src/realtime/index.ts
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{js,ts} : Ensure Promise-like statements are handled (awaited, returned, or chained) appropriately

Applied to files:

  • packages/react/src/realtime/index.ts
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to apps/web/src/hooks/use*.{ts,tsx} : Name React hooks with a use* prefix and place them in apps/web/src/hooks

Applied to files:

  • packages/react/src/support/hooks/use-conversation.ts
  • packages/react/src/support/hooks/use-send-message.ts
  • packages/react/src/hooks/index.ts
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{js,ts} : Don't have unused imports

Applied to files:

  • packages/react/src/provider.tsx
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to **/*.{ts,tsx} : Prefer explicit return types on exported APIs in TypeScript

Applied to files:

  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/index.ts
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.{ts,tsx} : Export type definitions for the public API

Applied to files:

  • packages/react/src/hooks/utils/use-client-query.ts
  • packages/react/src/index.ts
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{ts,tsx} : Use export type for types

Applied to files:

  • packages/react/src/hooks/utils/use-client-query.ts
📚 Learning: 2025-09-17T09:00:28.751Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T09:00:28.751Z
Learning: Applies to packages/react/src/**/*.{ts,tsx} : Develop headless React components (including <Support /> and primitives) under packages/react/src/

Applied to files:

  • packages/react/src/index.ts
🧬 Code graph analysis (7)
packages/react/src/realtime/index.ts (2)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts (1)
  • RealtimeEventHandlerContext (19-20)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts (1)
  • RealtimeEventHandlerContext (4-4)
packages/react/src/support/pages/conversation.tsx (4)
packages/react/src/hooks/use-conversation-messages.ts (1)
  • useConversationMessages (36-112)
packages/core/src/client.ts (1)
  • sendMessage (244-333)
packages/core/src/rest-client.ts (1)
  • sendMessage (576-622)
packages/react/src/hooks/use-send-message.ts (1)
  • useSendMessage (77-185)
packages/react/src/hooks/use-conversation.ts (4)
packages/types/src/api/conversation.ts (2)
  • GetConversationResponse (135-137)
  • GetConversationRequest (123-125)
packages/react/src/provider.tsx (1)
  • useSupport (221-238)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (38-219)
packages/react/src/provider.tsx (3)
packages/core/src/client.ts (1)
  • CossistantClient (66-349)
packages/react/src/hooks/utils/use-rest-client.ts (1)
  • useClient (18-51)
packages/react/src/hooks/store/use-website-store.ts (1)
  • useWebsiteStore (42-85)
packages/react/src/hooks/utils/use-client-query.ts (1)
packages/react/src/index.ts (1)
  • useClientQuery (38-38)
packages/react/src/hooks/use-conversation-messages.ts (4)
packages/core/src/store/messages-store.ts (1)
  • ConversationMessagesState (6-10)
packages/react/src/provider.tsx (1)
  • useSupport (221-238)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (38-219)
packages/react/src/hooks/store/use-website-store.ts (2)
packages/react/src/hooks/store/use-store-selector.ts (1)
  • useStoreSelector (10-37)
packages/react/src/hooks/utils/use-client-query.ts (1)
  • useClientQuery (38-219)
🔇 Additional comments (20)
packages/react/src/realtime/index.ts (3)

9-17: Context shape changed—verify no remaining context.queryClient usages

Dropping QueryClient from the base context risks stale consumers.

#!/usr/bin/env bash
# Find remaining references to the old context/queryClient
rg -nP '\bRealtimeEventHandlerContext\b|context\.\s*queryClient\b' -g '!**/node_modules/**'

57-74: Async handler rejections not caught; remove console usage

Current try/catch misses async rejections and violates “Don’t use console”.

-    for (const handler of eventHandlers) {
-      try {
-        handler({
-          event,
-          context,
-        });
-      } catch (error) {
-        if (onError) {
-          onError(error, event.type);
-        } else {
-          console.error("[Realtime] Event handler failed", {
-            error,
-            eventType: event.type,
-          });
-        }
-      }
-    }
+    for (const handler of eventHandlers) {
+      void Promise.resolve(
+        handler({ event, context })
+      ).catch((error) => {
+        onError?.(error, event.type);
+      });
+    }

As per coding guidelines.


121-133: Route transform failures via onTransformError only; no console

Avoid console logging in the transform error path.

-    } catch (error) {
-      if (onTransformError) {
-        onTransformError(error, params);
-      } else {
-        console.error("[Realtime] Failed to transform MESSAGE_CREATED event", {
-          error,
-          eventType: params.event.type,
-        });
-      }
-      return;
-    }
+    } catch (error) {
+      onTransformError?.(error, params);
+      return;
+    }

As per coding guidelines.

packages/react/src/support/hooks/use-conversation.ts (1)

1-6: Re-export looks good

Centralizing the hook API reduces duplication and keeps a single source of truth.

packages/react/src/support/pages/conversation.tsx (2)

116-120: Reset seen-tracking when the conversation changes (not only on mount)

Ensure refs don't leak across conversation switches.

- }, []);
+ }, [realConversationId]);

57-57: Gate messages query until a real conversation ID exists

Avoid 404s and error state when conversationId is the pending sentinel.

- const messagesQuery = useConversationMessages(conversationId);
+ const messagesQuery = useConversationMessages(conversationId, {
+   enabled: hasRealConversation,
+ });
packages/react/src/hooks/use-conversation.ts (1)

29-31: Guard against missing conversationsStore on the client

Throw a clear runtime error instead of failing inside useStoreSelector.

- const store = client.conversationsStore;
+ const store =
+   client.conversationsStore ??
+   ((): typeof client.conversationsStore => {
+     throw new Error(
+       "Conversations store is not available on the client instance"
+     );
+   })();
packages/react/src/hooks/store/use-website-store.ts (2)

1-1: Repo test failure: “Cannot find package 'nanoid' …/packages/core/src/utils.ts”

Add nanoid to the workspace (or core package) and ensure the import path matches runtime (browser vs node). Prefer nanoid/non-secure for client-only usage.


53-61: Fix data type mismatch: query returns Website, not the response envelope

Align useClientQuery generics and extract website in queryFn. This also makes initialData correct.

- const query = useClientQuery<PublicWebsiteResponse, { force?: boolean }>({
+ const query = useClientQuery<WebsiteState["website"], { force?: boolean }>({
     client,
-    queryFn: (instance, params) => instance.fetchWebsite(params ?? {}),
+    queryFn: async (instance, params) => {
+      const res = await instance.fetchWebsite(params ?? {});
+      return res.website;
+    },
     enabled: true,
     refetchInterval: options.refetchInterval ?? false,
     refetchOnWindowFocus: options.refetchOnWindowFocus ?? true,
     refetchOnMount: state.status === "idle",
     initialData: state.website ?? undefined,
   });
packages/react/src/provider.tsx (3)

6-6: Remove unused import useConversations.

It’s only referenced in commented code and will fail lint/biome.

Apply:

-import { useConversations } from "./hooks/use-conversations";

98-101: Call public API instead of private restClient.

Use client.setWebsiteContext(...) to avoid touching internals and the @ts-expect-error.

Apply:

-  if (website) {
-    // @ts-expect-error internal priming: safe in our library context
-    client.restClient?.setWebsiteContext?.(website.id, website.visitor?.id);
-  }
+  if (website) {
+    client.setWebsiteContext(website.id, website.visitor?.id);
+  }

As per coding guidelines (avoid private internals, clear runtime errors).


1-3: Remove the “add nanoid” suggestion—core already declares "nanoid": "^5.1.5"
Tests still report “Cannot find package 'nanoid'” because nanoid v5 is ESM-only and may not resolve under your test runner. Instead of adding the dependency, adjust your Jest/ts-jest configuration to support ESM imports (or pin to a CJS-compatible nanoid v3/4 or use nanoid/non-secure).

Likely an incorrect or invalid review comment.

packages/react/src/hooks/utils/use-client-query.ts (1)

9-19: Export the public option/result types.

These types are used by consumers and mentioned in the PR summary as re-exported. Without export, downstream imports will fail.

Apply:

-type UseClientQueryOptions<TData, TArgs> = {
+export type UseClientQueryOptions<TData, TArgs> = {
   client: CossistantClient;
   queryFn: QueryFn<TData, TArgs>;
   enabled?: boolean;
   refetchInterval?: number | false;
   refetchOnWindowFocus?: boolean;
   refetchOnMount?: boolean;
   initialData?: TData;
   initialArgs?: TArgs;
   dependencies?: readonly unknown[];
 };
 
-type UseClientQueryResult<TData, TArgs> = {
+export type UseClientQueryResult<TData, TArgs> = {
   data: TData | undefined;
   error: Error | null;
   isLoading: boolean;
   refetch: (args?: TArgs) => Promise<TData | undefined>;
 };

As per coding guidelines (export type for public API).

Also applies to: 21-26

packages/react/src/support/hooks/use-send-message.ts (1)

1-6: Re-export confirmed; upstream exports exist. All three types—SendMessageOptions, SendMessageResult, UseSendMessageResult—are defined with export type in packages/react/src/hooks/use-send-message.ts, so the barrel remains type-safe.

packages/react/src/support/hooks/index.ts (1)

1-20: LGTM on centralized re-exports.

Paths and type/value exports look consistent. Please confirm these are mirrored in packages/react/src/index.ts so consumers have a single stable entrypoint.

packages/react/src/hooks/use-conversation-messages.ts (1)

100-101: Don’t mask ongoing loads; surface real loading state.

This forces isLoading to false after the first page, hiding pagination/refresh loads. Use the query’s loading and, if needed, expose a separate isInitialLoading.

-	const isInitialLoad = selection.messages.length === 0;
-	const isLoading = isInitialLoad ? queryLoading : false;
+	const isInitialLoad = selection.messages.length === 0;
+	const isLoading = queryLoading;
+	const isInitialLoading = isInitialLoad && queryLoading;

Also add the field to the type and return value:

-export type UseConversationMessagesResult = ConversationMessagesState & {
+export type UseConversationMessagesResult = ConversationMessagesState & {
 	isLoading: boolean;
+	isInitialLoading: boolean;
 	error: Error | null;
@@
 	return {
 		messages: selection.messages,
 		hasNextPage: selection.hasNextPage,
 		nextCursor: selection.nextCursor,
 		isLoading,
+		isInitialLoading,
 		error,
 		refetch,
 		fetchNextPage,
 	};
packages/react/src/index.ts (4)

2-37: Good: type‑only re‑exports are correctly marked.

Using export type { ... } is consistent with our TS guidelines and keeps value/type namespaces clean.


48-53: Remove wildcard re-export from "./support" to prevent duplicate exports and leaking deprecated APIs.

export * from "./support" will re‑export useWebsite and its types, which you also explicitly export on Lines 49–53. This risks duplicate export errors and re‑exposes deprecated/legacy hooks that the PR intends to phase out.

Apply this diff:

- export * from "./support";
+ // Avoid re-exporting all of "./support" to keep the public API curated.

1-1: Remove missing ‘nanoid’ dependency suggestion: The nanoid package is already declared in packages/core/package.json (v^5.1.5); no changes needed here.

Likely an incorrect or invalid review comment.


44-44: No conflicting useClient exports in the React package. Verified that only the implementation in packages/react/src/hooks/utils/use-rest-client.ts and its single re-export in packages/react/src/index.ts exist—no other barrels or provider files export useClient.

Comment on lines +24 to +27
const keyFromEnv =
process.env.NEXT_PUBLIC_COSSISSTANT_KEY ||
process.env.COSSISSTANT_PUBLIC_KEY;
const keyToUse = publicKey ?? keyFromEnv;

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 | 🔴 Critical

Env var name typo breaks key resolution (throws even when env is set).

"COSSISSTANT" (triple s) is misspelled in both env var lookups. This prevents picking up correctly set keys.

Apply:

-		const keyFromEnv =
-			process.env.NEXT_PUBLIC_COSSISSTANT_KEY ||
-			process.env.COSSISSTANT_PUBLIC_KEY;
+		const keyFromEnv =
+			// correct names
+			process.env.NEXT_PUBLIC_COSSISTANT_KEY ??
+			process.env.COSSISTANT_PUBLIC_KEY ??
+			// legacy misspelling for back-compat
+			process.env.NEXT_PUBLIC_COSSISSTANT_KEY ??
+			process.env.COSSISSTANT_PUBLIC_KEY;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/react/src/hooks/utils/use-rest-client.ts around lines 24 to 27, the
environment variable lookups use the misspelled "COSSISSTANT" (triple s); change
both occurrences to the correct "COSSISTANT" spelling so the code reads
process.env.NEXT_PUBLIC_COSSISTANT_KEY || process.env.COSSISTANT_PUBLIC_KEY (or
keep whichever project convention you use for NEXT_PUBLIC_ vs non-public),
update any other occurrences in the repo, and run the app/tests to verify the
key resolution works.

Comment on lines 29 to 33
if (!keyToUse) {
const envKey =
process.env.NEXT_PUBLIC_COSSISSTANT_KEY ||
process.env.COSSISSTANT_PUBLIC_KEY;

if (!envKey) {
setError(
new Error(
"Public key is required. Please provide it as a prop or set NEXT_PUBLIC_COSSISSTANT_KEY environment variable."
)
);
return null;
}
keyToUse = envKey;
throw new Error(
"Public key is required. Please provide it as a prop or set NEXT_PUBLIC_COSSISSTANT_KEY environment variable."
);
}

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 | 🟡 Minor

Error message references the wrong env var name.

Align the guidance with the corrected env var(s).

-			throw new Error(
-				"Public key is required. Please provide it as a prop or set NEXT_PUBLIC_COSSISSTANT_KEY environment variable."
-			);
+			throw new Error(
+				"Public key is required. Pass publicKey or set NEXT_PUBLIC_COSSISTANT_KEY (or COSSISTANT_PUBLIC_KEY)."
+			);
📝 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 (!keyToUse) {
const envKey =
process.env.NEXT_PUBLIC_COSSISSTANT_KEY ||
process.env.COSSISSTANT_PUBLIC_KEY;
if (!envKey) {
setError(
new Error(
"Public key is required. Please provide it as a prop or set NEXT_PUBLIC_COSSISSTANT_KEY environment variable."
)
);
return null;
}
keyToUse = envKey;
throw new Error(
"Public key is required. Please provide it as a prop or set NEXT_PUBLIC_COSSISSTANT_KEY environment variable."
);
}
if (!keyToUse) {
throw new Error(
"Public key is required. Pass publicKey or set NEXT_PUBLIC_COSSISTANT_KEY (or COSSISTANT_PUBLIC_KEY)."
);
}
🤖 Prompt for AI Agents
In packages/react/src/hooks/utils/use-rest-client.ts around lines 29 to 33, the
thrown Error message references the wrong environment variable name; update the
message to reference the actual env var(s) used by the codebase (match the key
name(s) read from process.env, e.g. NEXT_PUBLIC_COSSISTANT_KEY) so the guidance
is correct and actionable — replace the incorrect NEXT_PUBLIC_COSSISSTANT_KEY
text with the correct env var name(s) and keep the rest of the message intact.

@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: 7

Caution

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

⚠️ Outside diff range comments (2)
packages/react/src/primitives/message-list.tsx (1)

60-143: Forwarded callback refs crash the auto-scroll logic

When a consumer passes a callback ref, ref is a function. Casting it to MutableRefObject keeps it as a function at runtime, so the later scrollRef.current.scrollTo(...) call throws. forwardRef must support both object and callback refs, so this is a correctness bug. Please merge the internal ref with the forwarded ref instead of assuming an object ref.

-		const internalRef = React.useRef<HTMLDivElement>(null);
-		const scrollRef =
-			(ref as React.MutableRefObject<HTMLDivElement>) || internalRef;
+		const internalRef = React.useRef<HTMLDivElement>(null);
+		const assignRef = React.useCallback(
+			(node: HTMLDivElement | null) => {
+				internalRef.current = node;
+
+				if (typeof ref === "function") {
+					ref(node);
+				} else if (ref) {
+					(ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
+				}
+			},
+			[ref],
+		);
@@
-			React.useEffect(() => {
-				if (autoScroll && scrollRef.current) {
+			React.useEffect(() => {
+				const element = internalRef.current;
+
+				if (autoScroll && element) {
@@
-						scrollRef.current.scrollTo({
-							top: scrollRef.current.scrollHeight,
+						element.scrollTo({
+							top: element.scrollHeight,
@@
-			}, [messages.length, events.length, autoScroll, scrollRef]);
+			}, [messages.length, events.length, autoScroll]);
@@
-					ref: scrollRef,
+					ref: assignRef,
packages/react/src/hooks/use-grouped-messages.ts (1)

249-260: Fix unread boundary detection in hasUnreadAfter. Line 258 currently returns messageIndex < lastReadIndex, so a viewer who has already read past a message gets true, while a viewer who still has unread messages (their lastReadIndex is behind the target) gets false. This inverts the intended logic and hides unread indicators exactly when unread messages exist. Re-check the messages that follow the given ID and bail as soon as you find one the viewer hasn’t seen.

-			hasUnreadAfter: (messageId: string, userId: string): boolean => {
-				const lastRead = lastReadMessageMap.get(userId);
-				if (!lastRead) {
-					return true; // All messages are unread
-				}
-
-				// Find the indices
-				const messageIndex = messages.findIndex((m) => m.id === messageId);
-				const lastReadIndex = messages.findIndex((m) => m.id === lastRead);
-
-				return messageIndex < lastReadIndex;
-			},
+			hasUnreadAfter: (messageId: string, userId: string): boolean => {
+				const startIndex = messages.findIndex((message) => message.id === messageId);
+				if (startIndex === -1) {
+					return false;
+				}
+
+				for (let index = startIndex + 1; index < messages.length; index++) {
+					const seenBy = seenByMap.get(messages[index].id);
+					if (!seenBy?.has(userId)) {
+						return true;
+					}
+				}
+
+				return false;
+			},
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0f711d and 56b2ac5.

📒 Files selected for processing (49)
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts (1 hunks)
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts (1 hunks)
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts (1 hunks)
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx (1 hunks)
  • apps/web/src/app/playground/page.tsx (1 hunks)
  • packages/core/src/client.ts (5 hunks)
  • packages/core/src/store/messages-store.test.ts (1 hunks)
  • packages/core/src/store/messages-store.ts (1 hunks)
  • packages/next/src/hooks/index.ts (1 hunks)
  • packages/next/src/hooks/use-client-query.ts (1 hunks)
  • packages/next/src/hooks/use-conversation-messages.ts (1 hunks)
  • packages/next/src/hooks/use-conversation.ts (1 hunks)
  • packages/next/src/hooks/use-conversations.ts (1 hunks)
  • packages/next/src/hooks/use-create-conversation.ts (1 hunks)
  • packages/next/src/hooks/use-grouped-messages.ts (1 hunks)
  • packages/next/src/hooks/use-multimodal-input.ts (1 hunks)
  • packages/next/src/hooks/use-realtime-support.ts (1 hunks)
  • packages/next/src/hooks/use-rest-client.ts (1 hunks)
  • packages/next/src/hooks/use-send-message.ts (1 hunks)
  • packages/next/src/index.ts (1 hunks)
  • packages/next/src/provider.tsx (1 hunks)
  • packages/next/src/support/index.tsx (1 hunks)
  • packages/react/src/config.tsx (2 hunks)
  • packages/react/src/hooks/index.ts (1 hunks)
  • packages/react/src/hooks/store/use-website-store.ts (1 hunks)
  • packages/react/src/hooks/use-client-query.ts (1 hunks)
  • packages/react/src/hooks/use-conversation-messages.ts (1 hunks)
  • packages/react/src/hooks/use-conversation.ts (1 hunks)
  • packages/react/src/hooks/use-conversations.ts (1 hunks)
  • packages/react/src/hooks/use-default-messages.ts (2 hunks)
  • packages/react/src/hooks/use-grouped-messages.ts (2 hunks)
  • packages/react/src/hooks/use-rest-client.ts (3 hunks)
  • packages/react/src/index.ts (1 hunks)
  • packages/react/src/primitives/avatar/avatar.tsx (1 hunks)
  • packages/react/src/primitives/avatar/fallback.tsx (1 hunks)
  • packages/react/src/primitives/avatar/image.tsx (1 hunks)
  • packages/react/src/primitives/bubble.tsx (1 hunks)
  • packages/react/src/primitives/button.tsx (1 hunks)
  • packages/react/src/primitives/message-group.tsx (6 hunks)
  • packages/react/src/primitives/message-list.tsx (4 hunks)
  • packages/react/src/primitives/message.tsx (3 hunks)
  • packages/react/src/primitives/multimodal-input.tsx (2 hunks)
  • packages/react/src/primitives/window.tsx (1 hunks)
  • packages/react/src/provider.tsx (6 hunks)
  • packages/react/src/realtime/support-provider.tsx (1 hunks)
  • packages/react/src/support/components/cossistant-branding.tsx (1 hunks)
  • packages/react/src/support/hooks/index.ts (1 hunks)
  • packages/react/src/support/index.tsx (2 hunks)
  • packages/react/src/support/pages/conversation.tsx (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/next/src/support/index.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/core/src/store/messages-store.ts
  • packages/react/src/support/hooks/index.ts
  • packages/react/src/hooks/use-conversations.ts
  • packages/react/src/support/pages/conversation.tsx
  • packages/react/src/hooks/use-conversation-messages.ts
  • packages/react/src/primitives/multimodal-input.tsx
  • packages/react/src/hooks/use-conversation.ts
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{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/next/src/hooks/use-client-query.ts
  • packages/next/src/provider.tsx
  • packages/next/src/hooks/index.ts
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/next/src/hooks/use-conversations.ts
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/next/src/index.ts
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/next/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/next/src/hooks/use-grouped-messages.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/next/src/hooks/use-rest-client.ts
  • packages/react/src/provider.tsx
  • packages/next/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/next/src/hooks/use-create-conversation.ts
  • packages/next/src/hooks/use-send-message.ts
  • packages/core/src/client.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/next/src/hooks/use-realtime-support.ts
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/index.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/next/src/hooks/use-client-query.ts
  • packages/next/src/provider.tsx
  • packages/next/src/hooks/index.ts
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/next/src/hooks/use-conversations.ts
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/next/src/index.ts
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/next/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/next/src/hooks/use-grouped-messages.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/next/src/hooks/use-rest-client.ts
  • packages/react/src/provider.tsx
  • packages/next/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/next/src/hooks/use-create-conversation.ts
  • packages/next/src/hooks/use-send-message.ts
  • packages/core/src/client.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/next/src/hooks/use-realtime-support.ts
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/index.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/next/src/hooks/use-client-query.ts
  • packages/next/src/hooks/index.ts
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/next/src/hooks/use-conversations.ts
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/next/src/index.ts
  • packages/next/src/hooks/use-conversation-messages.ts
  • packages/next/src/hooks/use-grouped-messages.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/next/src/hooks/use-rest-client.ts
  • packages/next/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/next/src/hooks/use-create-conversation.ts
  • packages/next/src/hooks/use-send-message.ts
  • packages/core/src/client.ts
  • packages/next/src/hooks/use-realtime-support.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/index.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/next/src/hooks/use-client-query.ts
  • packages/next/src/provider.tsx
  • packages/next/src/hooks/index.ts
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/next/src/hooks/use-conversations.ts
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/next/src/index.ts
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/next/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/next/src/hooks/use-grouped-messages.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/next/src/hooks/use-rest-client.ts
  • packages/react/src/provider.tsx
  • packages/next/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/next/src/hooks/use-create-conversation.ts
  • packages/next/src/hooks/use-send-message.ts
  • packages/core/src/client.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/next/src/hooks/use-realtime-support.ts
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/index.ts
**/*.{js,tsx,ts,jsx}

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

Use arrow functions instead of function expressions

Files:

  • packages/next/src/hooks/use-client-query.ts
  • packages/next/src/provider.tsx
  • packages/next/src/hooks/index.ts
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/next/src/hooks/use-conversations.ts
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/next/src/index.ts
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/next/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/next/src/hooks/use-grouped-messages.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/next/src/hooks/use-rest-client.ts
  • packages/react/src/provider.tsx
  • packages/next/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/next/src/hooks/use-create-conversation.ts
  • packages/next/src/hooks/use-send-message.ts
  • packages/core/src/client.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/next/src/hooks/use-realtime-support.ts
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/index.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/next/src/hooks/use-client-query.ts
  • packages/next/src/provider.tsx
  • packages/next/src/hooks/index.ts
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/next/src/hooks/use-conversations.ts
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/next/src/index.ts
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/next/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/next/src/hooks/use-grouped-messages.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/next/src/hooks/use-rest-client.ts
  • packages/react/src/provider.tsx
  • packages/next/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/next/src/hooks/use-create-conversation.ts
  • packages/next/src/hooks/use-send-message.ts
  • packages/core/src/client.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/next/src/hooks/use-realtime-support.ts
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/index.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/next/src/hooks/use-client-query.ts
  • packages/next/src/provider.tsx
  • packages/next/src/hooks/index.ts
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/next/src/hooks/use-conversations.ts
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/next/src/index.ts
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/next/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/next/src/hooks/use-grouped-messages.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/next/src/hooks/use-rest-client.ts
  • packages/react/src/provider.tsx
  • packages/next/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/next/src/hooks/use-create-conversation.ts
  • packages/next/src/hooks/use-send-message.ts
  • packages/core/src/client.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/next/src/hooks/use-realtime-support.ts
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/index.ts
packages/**

📄 CodeRabbit inference engine (AGENTS.md)

Put shared contracts and utilities in packages/* (e.g., core, types, transactional)

Files:

  • packages/next/src/hooks/use-client-query.ts
  • packages/next/src/provider.tsx
  • packages/next/src/hooks/index.ts
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/next/src/hooks/use-conversations.ts
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/next/src/index.ts
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/next/src/hooks/use-conversation-messages.ts
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/next/src/hooks/use-grouped-messages.ts
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/next/src/hooks/use-rest-client.ts
  • packages/react/src/provider.tsx
  • packages/next/src/hooks/use-conversation.ts
  • packages/react/src/hooks/index.ts
  • packages/next/src/hooks/use-create-conversation.ts
  • packages/next/src/hooks/use-send-message.ts
  • packages/core/src/client.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/next/src/hooks/use-realtime-support.ts
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/primitives/bubble.tsx
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/core/src/store/messages-store.test.ts
  • packages/react/src/index.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:

  • packages/next/src/provider.tsx
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/primitives/window.tsx
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/primitives/avatar/image.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.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:

  • packages/next/src/provider.tsx
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/primitives/window.tsx
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/primitives/avatar/image.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.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:

  • packages/next/src/provider.tsx
  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • packages/react/src/primitives/window.tsx
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/primitives/avatar/image.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.tsx
packages/react/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Develop headless React components (including and primitives) under packages/react/src/

Files:

  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/primitives/bubble.tsx
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/react/src/index.ts
packages/react/**/*.{ts,tsx}

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

packages/react/**/*.{ts,tsx}: Components must render with no built-in styles (headless, unstyled)
Components must accept className, style, and asChild props to enable external styling/control
Do not use emotion, styled-components, or style-prop APIs; styling is via Tailwind/CSS through className/style
Use useRenderElement to create elements and encapsulate render logic (supporting intrinsic tags and render prop overrides)
Components with internal state must be controllable via props (e.g., value/defaultValue/onChange or isOpen/onOpenChange)
Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child
May reference Base UI logic internally, but never expose Base UI or MUI APIs; do not leak getRootProps/getInputProps; prefer useRenderElement
Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components
Provide a render prop API: render?: (props, state) => ReactElement, and allow className to be string or (state) => string
Support developer-owned rendering: components should not impose visual structure and should allow parent-driven markup via render prop/asChild

Files:

  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/hooks/store/use-website-store.ts
  • packages/react/src/primitives/window.tsx
  • packages/react/src/support/index.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/react/src/support/components/cossistant-branding.tsx
  • packages/react/src/hooks/use-grouped-messages.ts
  • packages/react/src/provider.tsx
  • packages/react/src/hooks/index.ts
  • packages/react/src/primitives/avatar/image.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
  • packages/react/src/realtime/support-provider.tsx
  • packages/react/src/primitives/bubble.tsx
  • packages/react/src/hooks/use-default-messages.ts
  • packages/react/src/hooks/use-rest-client.ts
  • packages/react/src/index.ts
apps/web/src/app/**

📄 CodeRabbit inference engine (AGENTS.md)

Place Next.js App Router pages/layouts/routes under apps/web/src/app

Files:

  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
  • apps/web/src/app/playground/page.tsx
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts
  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
apps/web/src/{app,components}/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Use snake_case filenames for React components and layouts in the web app

Files:

  • apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
  • apps/web/src/app/playground/page.tsx
**/*.{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:

  • packages/core/src/store/messages-store.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:

  • packages/core/src/store/messages-store.test.ts
🧠 Learnings (20)
📚 Learning: 2025-09-17T16:59:53.676Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/react-package-rules.mdc:0-0
Timestamp: 2025-09-17T16:59:53.676Z
Learning: Applies to packages/react/**/*.{ts,tsx} : Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child

Applied to files:

  • packages/next/src/hooks/use-client-query.ts
  • packages/next/src/provider.tsx
  • packages/react/src/primitives/window.tsx
  • packages/next/src/hooks/use-multimodal-input.ts
  • packages/react/src/hooks/index.ts
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.{ts,tsx} : Manage shared component state via React Context

Applied to files:

  • packages/next/src/provider.tsx
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.tsx : Root component acts as main context provider, manages global state, and handles initialization

Applied to files:

  • packages/next/src/provider.tsx
📚 Learning: 2025-09-17T16:59:53.676Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/react-package-rules.mdc:0-0
Timestamp: 2025-09-17T16:59:53.676Z
Learning: Applies to packages/react/**/*.{ts,tsx} : Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components

Applied to files:

  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/primitives/bubble.tsx
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.tsx : Structure components with forwardRef and typed props/refs as shown (ComponentRef, HTMLAttributes, ReactNode)

Applied to files:

  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/primitives/window.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/react/src/primitives/avatar/fallback.tsx
  • packages/react/src/primitives/avatar/image.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/primitives/bubble.tsx
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.{ts,tsx} : Provide proper type definitions for all props and refs

Applied to files:

  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/config.tsx
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.tsx : Base component pattern: forwardRef, choose Slot or intrinsic element via asChild, pass ref/className and spread props

Applied to files:

  • packages/react/src/primitives/message-list.tsx
  • packages/react/src/primitives/button.tsx
  • packages/react/src/primitives/message-group.tsx
  • packages/react/src/primitives/avatar/avatar.tsx
  • packages/react/src/primitives/message.tsx
  • packages/react/src/primitives/bubble.tsx
📚 Learning: 2025-09-17T16:59:53.676Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/react-package-rules.mdc:0-0
Timestamp: 2025-09-17T16:59:53.676Z
Learning: Applies to packages/react/**/*.{ts,tsx} : Support developer-owned rendering: components should not impose visual structure and should allow parent-driven markup via render prop/asChild

Applied to files:

  • packages/react/src/primitives/message-list.tsx
📚 Learning: 2025-09-17T09:06:53.619Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.619Z
Learning: Applies to **/*.{jsx,tsx} : Always include a type attribute for button elements

Applied to files:

  • packages/react/src/primitives/button.tsx
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{js,ts} : Ensure Promise-like statements are handled (awaited, returned, or chained) appropriately

Applied to files:

  • packages/react/src/hooks/use-client-query.ts
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{js,ts} : Don't use async functions as Promise executors

Applied to files:

  • packages/react/src/hooks/use-client-query.ts
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to **/*.{ts,tsx} : Prefer explicit return types on exported APIs in TypeScript

Applied to files:

  • packages/react/src/hooks/use-client-query.ts
  • packages/react/src/index.ts
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.{ts,tsx} : Export type definitions for the public API

Applied to files:

  • packages/react/src/hooks/use-client-query.ts
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{ts,tsx} : Use export type for types

Applied to files:

  • packages/react/src/hooks/use-client-query.ts
📚 Learning: 2025-09-17T09:00:28.751Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T09:00:28.751Z
Learning: Support React/Next.js/Remix applications with a ready-to-use <Support /> component and primitive headless components

Applied to files:

  • packages/react/src/primitives/window.tsx
  • packages/react/src/support/index.tsx
  • packages/react/src/provider.tsx
  • packages/react/src/config.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/primitives/bubble.tsx
📚 Learning: 2025-09-17T09:00:28.751Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T09:00:28.751Z
Learning: Applies to packages/react/src/**/*.{ts,tsx} : Develop headless React components (including <Support /> and primitives) under packages/react/src/

Applied to files:

  • packages/react/src/support/index.tsx
  • packages/react/src/config.tsx
  • apps/web/src/app/playground/page.tsx
  • packages/react/src/index.ts
📚 Learning: 2025-09-17T09:06:53.620Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.620Z
Learning: Applies to **/*.{js,ts} : Don't have unused imports

Applied to files:

  • packages/react/src/provider.tsx
📚 Learning: 2025-09-20T07:23:05.629Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: AGENTS.md:0-0
Timestamp: 2025-09-20T07:23:05.629Z
Learning: Applies to apps/web/src/hooks/use*.{ts,tsx} : Name React hooks with a use* prefix and place them in apps/web/src/hooks

Applied to files:

  • packages/react/src/hooks/index.ts
📚 Learning: 2025-09-17T09:06:53.619Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-09-17T09:06:53.619Z
Learning: Applies to **/*.{ts,tsx} : Don't use primitive type aliases or misleading types

Applied to files:

  • packages/react/src/primitives/message.tsx
📚 Learning: 2025-09-17T09:01:10.563Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-09-17T09:01:10.563Z
Learning: Applies to **/*.tsx : Child components must be used within Root, read context via hooks, support asChild, and follow consistent props

Applied to files:

  • packages/react/src/primitives/bubble.tsx
🧬 Code graph analysis (19)
packages/react/src/primitives/message-list.tsx (1)
packages/react/src/support/components/message-list.tsx (1)
  • MessageListProps (18-28)
packages/react/src/primitives/button.tsx (1)
packages/react/src/support/components/button.tsx (1)
  • ButtonProps (36-37)
packages/react/src/primitives/message-group.tsx (1)
packages/react/src/support/components/message-group.tsx (1)
  • MessageGroupProps (20-25)
packages/react/src/hooks/use-client-query.ts (1)
packages/core/src/client.ts (1)
  • CossistantClient (66-367)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx (1)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts (1)
  • DashboardRealtimeContext (15-19)
packages/react/src/hooks/store/use-website-store.ts (2)
packages/core/src/client.ts (1)
  • CossistantClient (66-367)
packages/react/src/hooks/use-client-query.ts (1)
  • useClientQuery (38-219)
packages/react/src/support/index.tsx (1)
packages/next/src/support/index.tsx (1)
  • SupportProps (4-4)
packages/react/src/primitives/avatar/fallback.tsx (1)
packages/react/src/primitives/avatar/index.parts.ts (1)
  • AvatarFallbackProps (3-3)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts (3)
packages/types/src/trpc/conversation.ts (1)
  • ConversationHeader (98-98)
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/types.ts (1)
  • RealtimeEventHandler (27-28)
apps/web/src/data/conversation-header-cache.ts (1)
  • updateConversationHeaderInCache (14-62)
packages/react/src/hooks/use-grouped-messages.ts (3)
packages/types/src/schemas.ts (1)
  • ConversationSeen (102-102)
packages/next/src/hooks/use-grouped-messages.ts (1)
  • UseGroupedMessagesProps (7-7)
packages/react/src/support/hooks/index.ts (1)
  • UseGroupedMessagesProps (10-10)
packages/react/src/provider.tsx (3)
packages/core/src/client.ts (1)
  • CossistantClient (66-367)
packages/react/src/hooks/use-rest-client.ts (1)
  • useClient (18-51)
packages/react/src/hooks/store/use-website-store.ts (1)
  • useWebsiteStore (42-85)
packages/core/src/client.ts (8)
packages/types/src/schemas.ts (2)
  • Conversation (58-58)
  • Message (25-25)
packages/core/src/rest-client.ts (2)
  • CossistantRestClient (43-623)
  • request (149-173)
packages/core/src/store/conversations-store.ts (2)
  • ConversationsStore (190-193)
  • createConversationsStore (195-209)
packages/core/src/store/messages-store.ts (2)
  • MessagesStore (188-198)
  • createMessagesStore (200-229)
packages/core/src/store/website-store.ts (3)
  • WebsiteStore (39-44)
  • createWebsiteStore (46-105)
  • WebsiteState (10-14)
packages/core/src/utils.ts (2)
  • generateConversationId (13-16)
  • generateMessageId (18-20)
packages/types/src/enums.ts (6)
  • ConversationStatus (17-21)
  • ConversationStatus (23-24)
  • MessageType (1-5)
  • MessageType (7-7)
  • MessageVisibility (33-36)
  • MessageVisibility (74-75)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (94-98)
packages/react/src/primitives/avatar/image.tsx (1)
packages/react/src/primitives/avatar/index.parts.ts (1)
  • AvatarImageProps (5-5)
packages/react/src/primitives/message.tsx (2)
apps/web/src/components/conversation/messages/message.tsx (1)
  • MessageProps (10-14)
packages/react/src/support/components/message.tsx (1)
  • MessageProps (10-14)
packages/react/src/config.tsx (1)
packages/react/src/support/store/support-store.ts (1)
  • SupportConfig (24-35)
packages/react/src/realtime/support-provider.tsx (4)
packages/core/src/client.ts (1)
  • CossistantClient (66-367)
packages/react/src/realtime/index.ts (2)
  • RealtimeEventHandlerContext (9-9)
  • RealtimeEventHandlersMap (26-28)
packages/react/src/provider.tsx (1)
  • useSupport (220-237)
packages/react/src/hooks/use-realtime-support.ts (1)
  • useRealtimeSupport (23-47)
apps/web/src/app/playground/page.tsx (6)
packages/react/src/provider.tsx (1)
  • DefaultMessage (26-30)
packages/types/src/enums.ts (2)
  • SenderType (9-13)
  • SenderType (15-15)
packages/react/src/hooks/use-realtime-support.ts (1)
  • useRealtimeSupport (23-47)
packages/react/src/support/index.tsx (2)
  • useSupportConfig (64-64)
  • Support (26-59)
packages/react/src/support/store/support-store.ts (1)
  • useSupportConfig (173-182)
apps/web/src/components/ui/logo.tsx (1)
  • LogoText (26-38)
packages/react/src/hooks/use-rest-client.ts (1)
packages/core/src/client.ts (1)
  • CossistantClient (66-367)
packages/core/src/store/messages-store.test.ts (5)
packages/types/src/realtime-events.ts (1)
  • RealtimeEvent (94-98)
packages/core/src/store/messages-store.ts (1)
  • createMessagesStore (200-229)
packages/core/src/client.ts (1)
  • CossistantClient (66-367)
packages/types/src/api/message.ts (1)
  • SendMessageRequest (37-37)
packages/types/src/api/conversation.ts (1)
  • CreateConversationRequestBody (35-37)
🪛 GitHub Actions: PR Tests
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts

[error] 1-1: Formatter would have reformatted file. Biome/Prettier detected style differences and would print updated content.

apps/web/src/app/playground/page.tsx

[error] 1-1: Formatter would have reformatted file. Biome/Prettier detected style differences and would print updated content.

apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts

[error] 1-1: Formatter would have reformatted file. Biome/Prettier detected style differences and would print updated content.

⏰ 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

Comment on lines +28 to 34
const input = (maybeInput as QueryKeyInput).input;
if (!input || typeof input !== "object") {
return null;
}

return input;
return input;
}

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 | 🔴 Critical

Do not skip cache updates when the TRPC input is undefined.

trpc-react-query emits keys like [["conversation","listConversationsHeaders"], { input: undefined, … }] when callers rely on server defaults. The new guard treats this as “no input” and bails out, so every dashboard view that omits websiteSlug will stop receiving CONVERSATION_SEEN updates and unread counts/last-seen stamps will stall. Please treat null/undefined as an empty input instead of aborting.

-  const input = (maybeInput as QueryKeyInput).input;
-  if (!input || typeof input !== "object") {
-    return null;
-  }
-
-  return input;
+  const input = (maybeInput as QueryKeyInput).input;
+  if (input == null) {
+    return {};
+  }
+  if (typeof input !== "object") {
+    return null;
+  }
+  return input;
📝 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
const input = (maybeInput as QueryKeyInput).input;
if (!input || typeof input !== "object") {
return null;
}
return input;
return input;
}
const input = (maybeInput as QueryKeyInput).input;
if (input == null) {
return {};
}
if (typeof input !== "object") {
return null;
}
return input;
}
🤖 Prompt for AI Agents
In
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/conversation-seen.ts
around lines 28 to 34, the guard currently treats missing TRPC input as "no
input" and returns null, which skips cache updates when callers pass undefined;
instead, treat null/undefined as an empty input object so updates still apply.
Change the logic to return an empty object ({}) when input is null or undefined
(while keeping the existing object-type check for other non-object inputs),
ensuring the function returns a usable input for cache keys rather than
aborting.

Comment on lines +3 to 6
RealtimeEventHandler,
RealtimeEventHandlerContext,
RealtimeEventHandlerParams,
} from "./types";

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 | 🟡 Minor

Restore Biome-compliant indentation to unblock CI.

Biome defaults to tab indentation for TypeScript, so the leading spaces on the exported types trigger the formatter check that’s failing in CI. Please re-run the formatter (e.g., pnpm biome format apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts) or adjust the indentation back to tabs so the pipeline passes. (biomejs.dev)

Apply this diff:

 export type {
-  RealtimeEventHandler,
-  RealtimeEventHandlerContext,
-  RealtimeEventHandlerParams,
+	RealtimeEventHandler,
+	RealtimeEventHandlerContext,
+	RealtimeEventHandlerParams,
 } from "./types";
📝 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
RealtimeEventHandler,
RealtimeEventHandlerContext,
RealtimeEventHandlerParams,
} from "./types";
// apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts
export type {
RealtimeEventHandler,
RealtimeEventHandlerContext,
RealtimeEventHandlerParams,
} from "./types";
🤖 Prompt for AI Agents
In apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts around
lines 3 to 6, the exported type imports have leading spaces instead of tabs
which breaks the Biome formatter; replace the leading spaces with tab
indentation for those lines (or run the project formatter) so the file uses
Biome's default tab indentation—e.g., convert the leading spaces to tabs for the
RealtimeEventHandler, RealtimeEventHandlerContext, and
RealtimeEventHandlerParams exports and then run `pnpm biome format
apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/index.ts` (or `pnpm
format`) to apply and verify the fix.

Comment on lines 1 to 81
"use client";

import { useRealtimeSupport } from "@cossistant/react/hooks/use-realtime-support";
import type { DefaultMessage } from "@cossistant/react/provider";
import Support, { useSupportConfig } from "@cossistant/react/support";
import type { DefaultMessage } from "@cossistant/next";
import { useRealtimeSupport } from "@cossistant/next/hooks/use-realtime-support";
import { Support, useSupportConfig } from "@cossistant/next/support";
import { SenderType } from "@cossistant/types";
import { motion } from "motion/react";
import Image from "next/image";
import { Logo, LogoText } from "@/components/ui/logo";
import { LogoText } from "@/components/ui/logo";

function PlaygroundPropDisplay({
name,
value,
name,
value,
}: {
name: string;
value: string;
name: string;
value: string;
}) {
return (
<div className="flex items-center justify-between gap-4 font-medium uppercase">
<h2 className="text-primary/80 text-sm">{name}</h2>
<div className="h-[1px] flex-1 bg-primary/10" />
<p className="font-mono text-sm">{value}</p>
</div>
);
return (
<div className="flex items-center justify-between gap-4 font-medium uppercase">
<h2 className="text-primary/80 text-sm">{name}</h2>
<div className="h-[1px] flex-1 bg-primary/10" />
<p className="font-mono text-sm">{value}</p>
</div>
);
}

const DEFAULT_MESSAGES: DefaultMessage[] = [
{
content: "Hi 👋 I'm Anthony, founder of Cossistant",
senderType: SenderType.TEAM_MEMBER,
},
{
content: "How can I help you?",
senderType: SenderType.TEAM_MEMBER,
},
{
content: "Hi 👋 I'm Anthony, founder of Cossistant",
senderType: SenderType.TEAM_MEMBER,
},
{
content: "How can I help you?",
senderType: SenderType.TEAM_MEMBER,
},
];

const QUICK_OPTIONS = ["How to install Cossistant?", "Pricing"];

export default function Playground() {
const { isConnected } = useRealtimeSupport();
const { mode, size, isOpen } = useSupportConfig();
const { isConnected } = useRealtimeSupport();
const { mode, size, isOpen } = useSupportConfig();

return (
<div className="relative flex min-h-screen items-center bg-background p-20">
<motion.div
animate={{ opacity: 1 }}
initial={{ opacity: 0 }}
transition={{ duration: 3 }}
>
<Image
alt="Playground"
className="absolute inset-0 z-0 object-cover opacity-10 grayscale-100"
height={2400}
src="https://cdn.cossistant.com/neom-t3dlLOhdEzs-unsplash.jpg"
width={1600}
/>
</motion.div>
<div className="z-10 h-fit w-full border border-primary/10 border-dashed p-4 md:w-1/3">
<h1 className="flex items-center gap-2 font-f37-stout text-xl">
<LogoText />
<span className="font-medium text-primary/40">dev playground</span>
</h1>
<div className="mt-10 flex flex-col gap-2">
<PlaygroundPropDisplay
name="Websocket healthy"
value={isConnected.toString()}
/>
<PlaygroundPropDisplay name="Opened" value={isOpen.toString()} />
<PlaygroundPropDisplay name="Mode" value={mode} />
<PlaygroundPropDisplay name="Size" value={size} />
</div>
</div>
<Support
defaultMessages={DEFAULT_MESSAGES}
defaultOpen
quickOptions={QUICK_OPTIONS}
/>
</div>
);
return (
<div className="relative flex min-h-screen items-center bg-background p-20">
<motion.div
animate={{ opacity: 1 }}
initial={{ opacity: 0 }}
transition={{ duration: 3 }}
>
<Image
alt="Playground"
className="absolute inset-0 z-0 object-cover opacity-10 grayscale-100"
height={2400}
src="https://cdn.cossistant.com/neom-t3dlLOhdEzs-unsplash.jpg"
width={1600}
/>
</motion.div>
<div className="z-10 h-fit w-full border border-primary/10 border-dashed p-4 md:w-1/3">
<h1 className="flex items-center gap-2 font-f37-stout text-xl">
<LogoText />
<span className="font-medium text-primary/40">dev playground</span>
</h1>
<div className="mt-10 flex flex-col gap-2">
<PlaygroundPropDisplay
name="Websocket healthy"
value={isConnected.toString()}
/>
<PlaygroundPropDisplay name="Opened" value={isOpen.toString()} />
<PlaygroundPropDisplay name="Mode" value={mode} />
<PlaygroundPropDisplay name="Size" value={size} />
</div>
</div>
<Support
defaultMessages={DEFAULT_MESSAGES}
defaultOpen
quickOptions={QUICK_OPTIONS}
/>
</div>
);
}

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 | 🟡 Minor

Run Biome formatter to fix pipeline failure. Biome flagged this file during CI (Formatter would have reformatted file), so please re-run the formatter (e.g., pnpm biome format apps/web/src/app/playground/page.tsx) to align with the enforced style and unblock the build.

🧰 Tools
🪛 GitHub Actions: PR Tests

[error] 1-1: Formatter would have reformatted file. Biome/Prettier detected style differences and would print updated content.

🤖 Prompt for AI Agents
In apps/web/src/app/playground/page.tsx around lines 1 to 81, the file fails CI
because Biome formatting differs from the committed file; run the Biome
formatter to update the file. Fix by running the project formatter command
(e.g., pnpm biome format apps/web/src/app/playground/page.tsx or pnpm biome
format .) then review the changes, stage and commit the formatted file so the
pipeline passes.

import type { Conversation, Message, RealtimeEvent } from "@cossistant/types";
import type { CreateConversationRequestBody } from "@cossistant/types/api/conversation";
import type { SendMessageRequest } from "@cossistant/types/api/message";
import { CossistantClient } from "../client";

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 | 🔴 Critical

Blocker: missing nanoid dependency breaks the suite

Instantiating CossistantClient pulls in packages/core/src/utils.ts, which now imports nanoid. The package isn’t listed in packages/core dependencies, so bun test fails with Cannot find package 'nanoid'. Please add the runtime dependency (and types if needed) to packages/core/package.json or stop requiring it before we can land this PR.

🤖 Prompt for AI Agents
In packages/core/src/store/messages-store.test.ts around line 5, running tests
fails because CossistantClient imports packages/core/src/utils.ts which now
requires the 'nanoid' package but packages/core/package.json does not list it;
add "nanoid" to the dependencies of packages/core/package.json (and add
@types/nanoid to devDependencies if TypeScript needs types) or remove/guard the
import in utils.ts so tests no longer require nanoid at runtime before landing
this PR.

Comment on lines +11 to 13
type UseDefaultMessagesParams = {
conversationId: 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 | 🟠 Major

Export the params type alongside the public hook

Since useDefaultMessages is part of the public React surface, its parameter contract needs to be exported for consumers to reuse and compose typings. Please switch the local alias to an exported type. citecoding_guidelines

-type UseDefaultMessagesParams = {
+export type UseDefaultMessagesParams = {
 	conversationId: string;
 };
📝 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
type UseDefaultMessagesParams = {
conversationId: string;
};
export type UseDefaultMessagesParams = {
conversationId: string;
};
🤖 Prompt for AI Agents
In packages/react/src/hooks/use-default-messages.ts around lines 11 to 13, the
params type for the public hook is currently declared locally; change it to an
exported type so consumers can reuse it by replacing the local alias with
"export type UseDefaultMessagesParams = { conversationId: string }" (and update
any local references/imports if needed).

Comment on lines +11 to +12
export { useClientQuery } from "./hooks/use-client-query";
export type {

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 | 🟠 Major

Re-export useClientQuery option/result types.

The PR summary promises these types, but the index only exports the hook. Please add the matching type re-export so consumers can grab them from the package root. As per coding guidelines.

-export { useClientQuery } from "./hooks/use-client-query";
+export { useClientQuery } from "./hooks/use-client-query";
+export type {
+	UseClientQueryOptions,
+	UseClientQueryResult,
+} from "./hooks/use-client-query";
📝 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
export { useClientQuery } from "./hooks/use-client-query";
export type {
export { useClientQuery } from "./hooks/use-client-query";
export type {
UseClientQueryOptions,
UseClientQueryResult,
} from "./hooks/use-client-query";
🤖 Prompt for AI Agents
In packages/react/src/index.ts around lines 11-12, the package re-exports the
hook but not its option/result types promised in the PR; add a type re-export
for the hook's option and result types by exporting them from
"./hooks/use-client-query" (e.g. export type { UseClientQueryOptions,
UseClientQueryResult } from "./hooks/use-client-query") so consumers can import
these types from the package root.

Comment on lines +3 to +7
type CossistantLogoProps = {
className?: string;
};

export function CossistantLogo({ className }: Props) {
export function CossistantLogo({ className }: CossistantLogoProps) {

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 | 🔴 Critical

Export the new props type.

CossistantLogoProps is intended to be part of the public surface, but it's currently module-private. Any index barrel trying to re-export it (or downstream consumers importing it) will hit a TypeScript error that the symbol doesn't exist. Please export the alias directly from this module.

-type CossistantLogoProps = {
+export type CossistantLogoProps = {
 	className?: string;
 };
📝 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
type CossistantLogoProps = {
className?: string;
};
export function CossistantLogo({ className }: Props) {
export function CossistantLogo({ className }: CossistantLogoProps) {
++ b/packages/react/src/support/components/cossistant-branding.tsx
@@
export type CossistantLogoProps = {
className?: string;
};
export function CossistantLogo({ className }: CossistantLogoProps) {
🤖 Prompt for AI Agents
In packages/react/src/support/components/cossistant-branding.tsx around lines 3
to 7, the type alias CossistantLogoProps is declared but not exported, causing
downstream consumers and index barrels to fail to re-export or import it; export
the type by changing the declaration to an exported type (e.g., export type
CossistantLogoProps = { className?: string } ) and keep the component signature
as-is so consumers can import both the component and its props from this module.

wasConversationCreated: true;
};
} catch (error) {
this.messagesStore.removeMessage(rest.conversationId, optimisticId);

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: Unvalidated Empty Array Causes Undefined Message

In the sendMessage method, when creating a new conversation, the code attempts to extract a message from response.initialMessages without validating if the array is empty. If the server returns an empty initialMessages array (which can happen if no default messages are provided), the message field in the return object becomes undefined. This violates the expected return type and can lead to runtime errors.

Fix in Cursor Fix in Web

This branch had an error being deployed

3 failed deployments
Production — 0ce55e78 Deployed Sep 28, 2025 by vercel[bot]
orginami-mono (Cossistant Prod / production) — 0ce55e78 Deployed Sep 28, 2025 by railway-app[bot]
Preview — 0ce55e78 Deployed Sep 28, 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