Skip to content

Conversation

duckduckhero
Copy link
Collaborator

No description provided.

Copy link

coderabbitai bot commented Sep 8, 2025

📝 Walkthrough

Walkthrough

Replaces the legacy chat pipeline with a new Chat V2: introduces UIMessage/UIMessageComponent, new hooks (useChat2, useChatQueries2), a CustomChatTransport with MCP tool loading, DB schemas/ops for conversations and v2 messages, message-prep utilities, and removes legacy chat components/hooks.

Changes

Cohort / File(s) Summary
Ignore/Meta
./.gitignore
Add .cursor/ to ignore list.
Chat input tweak
apps/desktop/.../chat/chat-input.tsx
Exclude mentions of type "selection" when extracting mentionedContent.
Removed legacy message UI
apps/desktop/.../chat/chat-message.tsx, apps/desktop/.../chat/message-content.tsx
Delete ChatMessage and MessageContent components and their exports.
New UI message renderer & small UI tweak
apps/desktop/.../chat/ui-message.tsx, apps/desktop/.../chat/markdown-card.tsx
Add UIMessageComponent and TextContent (markdown/tool-state rendering); remove mt-4/mb-4 from MarkdownCard.
Messages view update
apps/desktop/.../chat/chat-messages-view.tsx
Switch to UIMessage[] + UIMessageComponent; replace lifecycle props with isSubmitted/isStreaming/isReady/isError; adjust thinking indicator logic/layout.
Chat module exports
apps/desktop/.../chat/index.ts
Remove exports for ChatMessage, MarkdownCard, MessageContent; add export for UIMessageComponent.
Right-panel view integration
apps/desktop/.../views/chat-view.tsx
Migrate to per-conversation flow; use useChat2/useChatQueries2; add license gating, conversation creation, and updated handlers.
Hooks — add/remove
apps/desktop/.../hooks/useChat2.ts, .../useChatQueries2.ts, .../useChatLogic.ts, .../useChatQueries.ts
Add useChat2 and useChatQueries2; remove legacy useChatLogic and useChatQueries.
Transport (new)
apps/desktop/.../utils/chat-transport.ts
Add CustomChatTransport implementing ChatTransport<UIMessage> with model init, MCP tool discovery/loading, tool composition, streaming, and lifecycle management.
Chat utilities refactor
apps/desktop/.../utils/chat-utils.ts, apps/desktop/.../utils/markdown-parser.ts
Replace prepareMessageHistory with cleanUIMessages and prepareMessagesForAI; introduce ParsedPart and update parseMarkdownBlocks return type to ParsedPart[].
Tool behavior tweak
apps/desktop/.../utils/tools/edit_enhanced_note.ts
Soften session-store lookup; if session missing, warn and continue using editor ref.
DB: migrations, types, ops
crates/db-user/src/chat_conversations_migration.sql, .../chat_messages_v2_migration.sql, .../chat_conversations_types.rs, .../chat_messages_v2_types.rs, .../chat_conversations_ops.rs, .../chat_messages_v2_ops.rs, crates/db-user/src/lib.rs, crates/db-user/src/sessions_ops.rs
Add chat_conversations and chat_messages_v2 schemas, types, and CRUD ops; re-export modules; remove cascade deletion of chat groups/messages in session deletion.
DB plugin wiring
plugins/db/build.rs, plugins/db/src/commands/chats_v2.rs, plugins/db/src/commands/mod.rs, plugins/db/src/lib.rs
Register and expose Tauri commands for conversations and v2 messages (create/list conversations, create/list/update message v2).
AI SDK exposure & deps
packages/utils/src/ai.ts, package.json
Add @ai-sdk/react dependency; re-export useChat and types (ChatTransport, UIMessage, convertToModelMessages, etc.).
Build/plugin metadata
plugins/db/build.rs
Register new plugin commands: create_conversation, list_conversations, create_message_v2, list_messages_v2, update_message_v2_parts.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as ChatView (React)
  participant Q as useChatQueries2
  participant DB as DB Plugin (Tauri)
  participant C as useChat2
  participant T as CustomChatTransport
  participant M as Model (AI SDK)
  participant MCP as MCP Clients

  UI->>Q: load conversations (sessionId,userId)
  Q->>DB: list_conversations + list_messages_v2
  DB-->>Q: conversations + UIMessage[]
  Q-->>UI: conversations + set currentConversationId

  UI->>C: sendMessage({text, mentions, selection, html})
  C->>DB: create_message_v2 (user)
  C->>T: sendMessages(prepared messages, options)

  T->>MCP: load tools (conditional)
  T->>M: streamText(prepared messages + tools)
  M-->>T: streamed chunks (text / tool events)
  T-->>C: UIMessageChunk stream/events
  C-->>UI: incremental message updates

  M-->>T: final assistant message
  T-->>C: done
  C->>DB: create_message_v2 (assistant)
  C-->>UI: status=ready
Loading
sequenceDiagram
  autonumber
  participant Prep as prepareMessagesForAI
  participant Ctx as Session/Participants/Calendar
  participant Conv as convertToModelMessages

  Prep->>Ctx: fetch/derive session data, participants, events
  Prep->>Prep: build system prompt (chat.system)
  Prep->>Conv: convert UIMessage[] -> model messages
  Conv-->>Prep: model messages
  Prep->>Prep: enrich last user with mentions/selection (chat.user)
  Prep-->>T: prepared message array
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Mcp redesign 2 #1361 — Large MCP/chat v2 redesign touching same hooks, UIMessage types, and transport/tool integrations.
  • Chat fix 0806 #1298 — Edits the old prepareMessageHistory flow that was removed and replaced by prepareMessagesForAI.
  • Chat minor fix #1215 — Modifies right-panel chat UI and hooks that are replaced/removed in this PR.

Pre-merge checks (2 warnings, 1 inconclusive)

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description is empty and provides no information about the objectives, scope, or high-level changes of the extensive chat refactor, making it difficult for reviewers to understand the context or rationale. Add a concise description summarizing the goals and key changes (for example, migrating chat logic to a new database with useChat2/useChatQueries2 hooks, UI component updates, and new database migrations) along with any required migration or setup steps.
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current PR title "Chat refactor new db final (with use chat)" is related to the changes but is vague, contains noise like "final" and parentheses, and does not clearly summarize the primary refactor of chat components to a new database schema and useChat hook in a concise, single sentence. Rename the pull request to a concise, descriptive title such as "Refactor chat module to use new database schema and useChat hooks" to clearly convey the main change without extraneous words.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
		  - name: "Undocumented Breaking Changes"
			  mode: "warning"
			  instructions: |
				  Flag potential breaking changes that are not documented:
				  1. Identify changes to public APIs/exports, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints (including removed/renamed items and changes to types, required params, return values, defaults, or behavior).
				  2. Ignore purely internal/private changes (e.g., code not exported from package entry points or marked internal).
				  3. Verify documentation exists: a "Breaking Change" section in the PR description and updates to CHANGELOG.md.

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chat-refactor-new-db-final

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

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

⚠️ Outside diff range comments (5)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (2)

154-163: Also exclude selection-type mentions from marks

Current logic filters selection mentions for nodes but not for marks, so selection mentions can still slip through.

       if (node.marks && Array.isArray(node.marks)) {
         node.marks.forEach((mark: any) => {
           if (mark.type === "mention" || mark.type === "mention-@") {
-            if (mark.attrs) {
+            if (mark.attrs && mark.attrs.type !== "selection") {
               mentions.push({
                 id: mark.attrs.id || mark.attrs["data-id"],
                 type: mark.attrs.type || mark.attrs["data-type"] || "note",
                 label: mark.attrs.label || mark.attrs["data-label"] || "Unknown",
               });
             }
           }
         });
       }

Also applies to: 165-177


15-30: Remove unused onKeyDown prop

It isn’t used by the component (only referenced in a deps array). Drop it to satisfy “no unused variables.”

 interface ChatInputProps {
   inputValue: string;
   onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
   onSubmit: (
     mentionedContent?: Array<{ id: string; type: string; label: string }>,
     selectionData?: SelectionData,
     htmlContent?: string,
   ) => void;
-  onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
   autoFocus?: boolean;
   entityId?: string;
   entityType?: BadgeType;
   onNoteBadgeClick?: () => void;
   isGenerating?: boolean;
   onStop?: () => void;
 }
@@
-  {
+  {
     inputValue,
     onChange,
     onSubmit,
-    onKeyDown,
     autoFocus = false,
@@
-  }, [editorRef.current?.editor, onKeyDown, handleSubmit, inputValue]);
+  }, [editorRef.current?.editor, handleSubmit, inputValue]);

Also applies to: 348-349

apps/desktop/src/components/right-panel/components/chat/markdown-card.tsx (1)

7-17: Drop unused props (isComplete, hasEnhancedNote)

They aren’t used; keep API lean per guidelines.

 interface MarkdownCardProps {
   content: string;
-  isComplete: boolean;
   sessionTitle?: string;
   onApplyMarkdown?: (markdownContent: string) => void;
-  hasEnhancedNote?: boolean;
 }
 
 export function MarkdownCard(
-  { content, isComplete, sessionTitle, onApplyMarkdown, hasEnhancedNote = false }: MarkdownCardProps,
+  { content, sessionTitle, onApplyMarkdown }: MarkdownCardProps,
 ) {
apps/desktop/src/components/right-panel/utils/markdown-parser.ts (1)

14-43: Code fences lose language and backticks (breaks rendering)

You strip ``` fences and any language tag; downstream Markdown renderers won’t see a code block.

-export const parseMarkdownBlocks = (text: string): ParsedPart[] => {
+export const parseMarkdownBlocks = (text: string): ParsedPart[] => {
   const parts: ParsedPart[] = [];
   let currentIndex = 0;
   let inMarkdownBlock = false;
   let markdownStart = -1;
+  let currentLang = "";
 
   for (let i = 0; i < text.length - 2; i++) {
     if (text.slice(i, i + 3) === "```") {
       if (!inMarkdownBlock) {
         // Starting a markdown block
         if (i > currentIndex) {
           // Add text before markdown
           const textContent = text.slice(currentIndex, i).trim();
           if (textContent) {
             parts.push({
               type: "text",
               content: textContent,
             });
           }
         }
-        markdownStart = i + 3;
+        // capture optional language until newline
+        const after = text.slice(i + 3);
+        const nl = after.indexOf("\n");
+        if (nl >= 0) {
+          currentLang = after.slice(0, nl).trim();
+          markdownStart = i + 3 + nl + 1;
+        } else {
+          currentLang = "";
+          markdownStart = i + 3;
+        }
         inMarkdownBlock = true;
         i += 2; // Skip the ```
       } else {
         // Ending a markdown block
-        const markdownContent = text.slice(markdownStart, i);
+        const inner = text.slice(markdownStart, i);
+        const fence = currentLang ? "```" + currentLang + "\n" : "```";
+        const markdownContent = `${fence}${inner}\n\`\`\``;
         parts.push({
           type: "markdown",
           content: markdownContent,
           isComplete: true,
         });
         inMarkdownBlock = false;
+        currentLang = "";
         currentIndex = i + 3;
         i += 2; // Skip the ```
       }
     }
   }
 
   // Handle remaining content
   if (inMarkdownBlock) {
     // Incomplete markdown block
-    const markdownContent = text.slice(markdownStart);
+    const inner = text.slice(markdownStart);
+    const fence = currentLang ? "```" + currentLang + "\n" : "```";
+    const markdownContent = `${fence}${inner}`;
     parts.push({
       type: "markdown",
       content: markdownContent,
       isComplete: false,
     });
   } else if (currentIndex < text.length) {

Also applies to: 46-67

apps/desktop/src/components/right-panel/utils/chat-utils.ts (1)

148-157: Sync chat-utils.ts tool gating with connectorCommands outputs
Update the gating in chat-utils.ts (apps/desktop/src/components/right-panel/utils/chat-utils.ts:148–157) to include type === "HyprLocal" and ensure the hard-coded modelId list matches every identifier emitted by connectorCommands.getLlmConnection and getCustomLlmModel (“gpt-4.1”, “openai/gpt-4.1”, “anthropic/claude-sonnet-4”, “openai/gpt-4o”, “gpt-4o”, “openai/gpt-5”, and pro.hyprnote.com Hypr Cloud).

🧹 Nitpick comments (25)
.gitignore (1)

19-20: Trim trailing whitespace on CLAUDE.md entry

Avoid accidental pattern mismatches in .gitignore by removing the trailing space.

-CLAUDE.md 
+CLAUDE.md
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (2)

281-309: Enter is handled twice (TipTap handleKeyDown and DOM keydown)

Both handlers submit on Enter; this can double-submit depending on event order. Keep one path.

-      const handleKeyDown = (event: KeyboardEvent) => {
+      const handleKeyDown = (event: KeyboardEvent) => {
         if (event.metaKey || event.ctrlKey) {
           if (["b", "i", "u", "k"].includes(event.key.toLowerCase())) {
             event.preventDefault();
             return;
           }
         }
-
-        if (event.key === "Enter" && !event.shiftKey) {
-          event.preventDefault();
-
-          if (inputValue.trim()) {
-            handleSubmit();
-          }
-        }
+        // Let TipTap's handleKeyDown (setOptions) own Enter behavior.
       };

Also applies to: 311-349


221-225: Avoid editorRef.current?.editor in hook deps

Refs are mutable; depending on editorRef.current?.editor can cause unnecessary effect churn. Capture once or gate on existence.

-  }, [chatInputRef]);
+  }, [chatInputRef]);
@@
-  }, [editorRef.current?.editor, inputValue, handleSubmit]);
+  }, [inputValue, handleSubmit]);

Also applies to: 281-309

package.json (1)

6-8: New dependency OK

@ai-sdk/react aligns with the UIMessage integration. Ensure all packages in the workspace share compatible React versions.

crates/db-user/src/sessions_ops.rs (1)

113-131: Delete messages in one SQL instead of row-by-row.

Replace the loop with a single DELETE for fewer round-trips and better perf.

-        let mut rows = conn
-            .query(
-                "SELECT id FROM chat_conversations WHERE session_id = ?",
-                vec![session_id.clone()],
-            )
-            .await?;
-
-        while let Some(row) = rows.next().await? {
-            let conversation_id: String = row.get(0)?;
-            conn.execute(
-                "DELETE FROM chat_messages_v2 WHERE conversation_id = ?",
-                vec![conversation_id],
-            )
-            .await?;
-        }
+        conn.execute(
+            "DELETE FROM chat_messages_v2 
+             WHERE conversation_id IN (
+               SELECT id FROM chat_conversations WHERE session_id = ?
+             )",
+            vec![session_id.clone()],
+        )
+        .await?;

Optional follow-up: add ON DELETE CASCADE on chat_messages_v2(conversation_id) to remove this app-side delete entirely.

crates/db-user/src/chat_conversations_types.rs (1)

3-12: Persist None as NULL, not empty string, for name.

Current create_conversation inserts name via unwrap_or_default(), losing the None vs "" distinction.

Proposed change in crates/db-user/src/chat_conversations_ops.rs:

-                "INSERT INTO chat_conversations (
-                    id, session_id, user_id, name, created_at, 
- updated_at
-                ) VALUES (?, ?, ?, ?, ?, ?)
-                RETURNING *",
-                vec![
-                    conversation.id,
-                    conversation.session_id,
-                    conversation.user_id,
-                    conversation.name.unwrap_or_default(),
-                    conversation.created_at.to_rfc3339(),
-                    conversation.updated_at.to_rfc3339(),
-                ],
+                "INSERT INTO chat_conversations (
+                    id, session_id, user_id, name, created_at, updated_at
+                ) VALUES (:id, :session_id, :user_id, :name, :created_at, :updated_at)
+                RETURNING *",
+                libsql::named_params! {
+                    ":id": conversation.id,
+                    ":session_id": conversation.session_id,
+                    ":user_id": conversation.user_id,
+                    ":name": conversation.name, // Option<String> -> NULL when None
+                    ":created_at": conversation.created_at.to_rfc3339(),
+                    ":updated_at": conversation.updated_at.to_rfc3339(),
+                },
crates/db-user/src/chat_messages_v2_migration.sql (1)

1-12: Add index for common query path.

Messages are listed by conversation_id ASC created_at; index will help.

 CREATE TABLE IF NOT EXISTS chat_messages_v2 (
@@
   FOREIGN KEY (conversation_id) REFERENCES chat_conversations(id)
 );
+
+-- Optimize listing messages by conversation and time
+CREATE INDEX IF NOT EXISTS idx_chat_messages_v2_conv_created_at
+  ON chat_messages_v2(conversation_id, created_at);

Optional: consider ON DELETE CASCADE on conversation_id to simplify deletes.

crates/db-user/src/chat_messages_v2_types.rs (1)

18-28: Avoid stringly-typed JSON for parts/metadata (future).

Consider serde_json::Value (or typed structs) at the type boundary; stringify only on DB I/O. This preserves validation and ergonomics.

crates/db-user/src/lib.rs (1)

145-172: Migrations appended correctly; count matches entries

Length is 26 and both chat_conversations/chat_messages_v2 migrations are appended without reordering. ✅

Please confirm both new SQL files are idempotent and safe to run on existing user data.

Additionally, consider adding follow-up migrations to create read-path indexes (optional, see advice below).

Suggested indexes (in separate, append-only migrations):

  • chat_conversations(session_id, updated_at DESC)
  • chat_messages_v2(conversation_id, created_at ASC)
crates/db-user/src/chat_messages_v2_ops.rs (1)

42-45: Add supporting index for read path

To keep chronological reads fast at scale, add an index on (conversation_id, created_at).

crates/db-user/src/chat_conversations_ops.rs (1)

33-54: List ordering optimized for recency

Ordering by updated_at DESC is appropriate for conversation lists.

Add an index on (session_id, updated_at DESC) to keep this fast with many conversations.

apps/desktop/src/components/right-panel/hooks/useChat2.ts (3)

147-149: Remove artificial delay before sending

updateOptions is synchronous; the 10ms sleep adds latency without guarantees. Safe to remove.

Apply this change:

-      // Small delay to ensure options are updated before tools are loaded
-      await new Promise(resolve => setTimeout(resolve, 10));

117-174: Include all used props in sendMessage dependencies to avoid stale captures

sendMessage uses sessions and onError but they are not in the dependency array; this can send stale context to the model.

Apply this change:

-    [sendAIMessage, conversationId],
+    [sendAIMessage, conversationId, sessions, onError],

176-190: Don’t gate updates by conversationId when updating by messageId

updateMessageV2Parts identifies the row by id; the extra conversationId check can block legitimate updates (e.g., system/tool messages).

Apply this change:

-    async (messageId: string, parts: any[]) => {
-      if (conversationId) {
-        try {
-          await dbCommands.updateMessageV2Parts(
-            messageId,
-            JSON.stringify(parts),
-          );
-        } catch (error) {
-          console.error("Failed to update message parts:", error);
-        }
-      }
-    },
-    [conversationId],
+    async (messageId: string, parts: any[]) => {
+      try {
+        await dbCommands.updateMessageV2Parts(
+          messageId,
+          JSON.stringify(parts),
+        );
+      } catch (error) {
+        console.error("Failed to update message parts:", error);
+      }
+    },
+    [],
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (1)

51-51: Use cross-env-safe timeout type.

NodeJS.Timeout can mis-type in DOM builds. Prefer ReturnType<typeof setTimeout>.

-  const thinkingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
+  const thinkingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
apps/desktop/src/components/right-panel/components/chat/ui-message.tsx (3)

168-174: Preserve whitespace between concatenated text parts.

Joining with no separator can mash sentences. Use a newline join.

-  const textParts = message.parts
+  const textParts = message.parts
       .filter(part => part.type === "text")
-      .map(part => part.text || "")
-      .join("");
+      .map(part => part.text || "")
+      .join("\n\n");

239-244: Type the tool part to remove any and fragile fallbacks.

Define a local ToolPart type and use it; improves safety and avoids "dynamic-tool" label leaks.

+type ToolPart = {
+  type: string;
+  toolName?: string;
+  state?: "input-streaming" | "input-available" | "output-available" | "output-error";
+  input?: unknown;
+  output?: unknown;
+  errorText?: string;
+};
@@
-          const toolPart = part as any;
+          const toolPart = part as ToolPart;
@@
-          if (toolPart.state === "output-available" && toolName !== "progress_update") {
+          if (toolPart.state === "output-available" && toolName !== "progress_update") {
@@
-          if (toolPart.state === "output-error" && toolName !== "progress_update") {
+          if (toolPart.state === "output-error" && toolName !== "progress_update") {

Additionally, compute the name as:

- const toolName = toolPart.toolName || part.type.replace("tool-", "");
+ const toolName =
+   toolPart.toolName ??
+   (part.type.startsWith("tool-") ? part.type.slice(5) : part.type);

Also applies to: 325-326, 435-436


295-314: Deduplicate repeated <pre> styles.

Extract a const preStyle = {...} and reuse; smaller VDOM diffs and easier tweaks.

+const preStyle: React.CSSProperties = {
+  backgroundColor: "rgb(249 250 251)",
+  border: "1px solid rgb(229 231 235)",
+  borderRadius: "6px",
+  padding: "8px 12px",
+  margin: 0,
+  paddingLeft: "24px",
+  fontSize: "0.6875rem",
+  fontFamily: "ui-monospace, SFMono-Regular, Consolas, monospace",
+  whiteSpace: "pre-wrap",
+  wordBreak: "break-word",
+  maxHeight: "200px",
+  overflow: "auto",
+  color: "rgb(75 85 99)",
+  lineHeight: 1.4,
+};
@@
-  <pre
-    style={{
-      backgroundColor: "rgb(249 250 251)",
-      border: "1px solid rgb(229 231 235)",
-      ...
-    }}
-  >
+  <pre style={preStyle}>

Also applies to: 370-388, 405-424

apps/desktop/src/components/right-panel/utils/chat-transport.ts (1)

108-136: Nit: narrow allMcpClients type.

Type to { close: () => void }[] to document intent without pulling concrete client types.

-  private allMcpClients: any[] = [];
+  private allMcpClients: Array<{ close: () => void }> = [];
apps/desktop/src/components/right-panel/hooks/useChatQueries2.ts (3)

23-26: Key queries by all inputs that affect the result.

Include userId in the queryKey so cache invalidation/refetching works if the user changes.

-    queryKey: ["conversations", sessionId],
+    queryKey: ["conversations", sessionId, userId],

32-47: N+1 database calls for previews.

Fetching all messages per conversation will not scale. Prefer a DB-side projection (first user message text + last message timestamp) or an aggregated query.


15-22: Tighten hook responsibility to reduce coupling.

The hook both fetches messages and optionally calls setMessages. This forced the caller to pass a no-op and re-fetch manually elsewhere. Consider returning data only and letting callers decide how to sync local state.

apps/desktop/src/components/right-panel/views/chat-view.tsx (2)

36-41: Messages aren’t cleared on entity switch.

Passing no-op setters into useActiveEntity prevents message clearing when the active entity changes, risking cross-entity leakage.

Option A (no reordering of hooks): add a tiny effect to clear chat on session change.

+  useEffect(() => {
+    setMessages([]);
+    setCurrentConversationId(null);
+  }, [sessionId, setMessages]);

Option B: if you later decouple hook order, pass setMessages to useActiveEntity.


246-248: Silence “unused parameter” lint cleanly.

Either remove the param or mark it as intentionally unused.

-  const handleSelectChat = (_chatId: string) => {
+  const handleSelectChat = () => {
     setShowHistory(false);
   };
apps/desktop/src/components/right-panel/utils/chat-utils.ts (1)

185-191: Return type can be narrowed for downstream safety.

preparedMessages is any[]. If the consumer expects OpenAI/Claude-style message shapes, type it accordingly to catch shape drift at compile time.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c7915f and 3663170.

⛔ Files ignored due to path filters (10)
  • plugins/db/js/bindings.gen.ts is excluded by !**/*.gen.ts
  • plugins/db/permissions/autogenerated/commands/create_conversation.toml is excluded by !plugins/**/permissions/**
  • plugins/db/permissions/autogenerated/commands/create_message_v2.toml is excluded by !plugins/**/permissions/**
  • plugins/db/permissions/autogenerated/commands/list_conversations.toml is excluded by !plugins/**/permissions/**
  • plugins/db/permissions/autogenerated/commands/list_messages_v2.toml is excluded by !plugins/**/permissions/**
  • plugins/db/permissions/autogenerated/commands/update_message_v2_parts.toml is excluded by !plugins/**/permissions/**
  • plugins/db/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/db/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/db/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (31)
  • .gitignore (1 hunks)
  • apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (1 hunks)
  • apps/desktop/src/components/right-panel/components/chat/chat-message.tsx (0 hunks)
  • apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (4 hunks)
  • apps/desktop/src/components/right-panel/components/chat/index.ts (1 hunks)
  • apps/desktop/src/components/right-panel/components/chat/markdown-card.tsx (1 hunks)
  • apps/desktop/src/components/right-panel/components/chat/message-content.tsx (0 hunks)
  • apps/desktop/src/components/right-panel/components/chat/ui-message.tsx (1 hunks)
  • apps/desktop/src/components/right-panel/hooks/useChat2.ts (1 hunks)
  • apps/desktop/src/components/right-panel/hooks/useChatLogic.ts (0 hunks)
  • apps/desktop/src/components/right-panel/hooks/useChatQueries.ts (0 hunks)
  • apps/desktop/src/components/right-panel/hooks/useChatQueries2.ts (1 hunks)
  • apps/desktop/src/components/right-panel/utils/chat-transport.ts (1 hunks)
  • apps/desktop/src/components/right-panel/utils/chat-utils.ts (5 hunks)
  • apps/desktop/src/components/right-panel/utils/markdown-parser.ts (1 hunks)
  • apps/desktop/src/components/right-panel/utils/tools/edit_enhanced_note.ts (1 hunks)
  • apps/desktop/src/components/right-panel/views/chat-view.tsx (6 hunks)
  • crates/db-user/src/chat_conversations_migration.sql (1 hunks)
  • crates/db-user/src/chat_conversations_ops.rs (1 hunks)
  • crates/db-user/src/chat_conversations_types.rs (1 hunks)
  • crates/db-user/src/chat_messages_v2_migration.sql (1 hunks)
  • crates/db-user/src/chat_messages_v2_ops.rs (1 hunks)
  • crates/db-user/src/chat_messages_v2_types.rs (1 hunks)
  • crates/db-user/src/lib.rs (5 hunks)
  • crates/db-user/src/sessions_ops.rs (1 hunks)
  • package.json (1 hunks)
  • packages/utils/src/ai.ts (1 hunks)
  • plugins/db/build.rs (1 hunks)
  • plugins/db/src/commands/chats_v2.rs (1 hunks)
  • plugins/db/src/commands/mod.rs (1 hunks)
  • plugins/db/src/lib.rs (1 hunks)
💤 Files with no reviewable changes (4)
  • apps/desktop/src/components/right-panel/hooks/useChatQueries.ts
  • apps/desktop/src/components/right-panel/hooks/useChatLogic.ts
  • apps/desktop/src/components/right-panel/components/chat/chat-message.tsx
  • apps/desktop/src/components/right-panel/components/chat/message-content.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

⚙️ CodeRabbit configuration file

**/*.{js,ts,tsx,rs}: 1. Do not add any error handling. Keep the existing one.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".

Files:

  • apps/desktop/src/components/right-panel/components/chat/markdown-card.tsx
  • plugins/db/src/lib.rs
  • plugins/db/src/commands/chats_v2.rs
  • crates/db-user/src/sessions_ops.rs
  • apps/desktop/src/components/right-panel/components/chat/chat-input.tsx
  • plugins/db/src/commands/mod.rs
  • apps/desktop/src/components/right-panel/utils/tools/edit_enhanced_note.ts
  • apps/desktop/src/components/right-panel/hooks/useChat2.ts
  • apps/desktop/src/components/right-panel/utils/chat-transport.ts
  • crates/db-user/src/chat_conversations_ops.rs
  • crates/db-user/src/chat_messages_v2_types.rs
  • crates/db-user/src/chat_conversations_types.rs
  • plugins/db/build.rs
  • apps/desktop/src/components/right-panel/components/chat/ui-message.tsx
  • crates/db-user/src/chat_messages_v2_ops.rs
  • apps/desktop/src/components/right-panel/utils/markdown-parser.ts
  • apps/desktop/src/components/right-panel/hooks/useChatQueries2.ts
  • apps/desktop/src/components/right-panel/components/chat/index.ts
  • packages/utils/src/ai.ts
  • apps/desktop/src/components/right-panel/utils/chat-utils.ts
  • crates/db-user/src/lib.rs
  • apps/desktop/src/components/right-panel/views/chat-view.tsx
  • apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx
🧬 Code graph analysis (12)
plugins/db/src/lib.rs (4)
plugins/sse/js/index.ts (1)
  • commands (4-77)
crates/db-user/src/chat_conversations_ops.rs (2)
  • create_conversation (4-31)
  • list_conversations (33-54)
plugins/db/src/commands/chats_v2.rs (5)
  • create_conversation (6-21)
  • list_conversations (26-41)
  • create_message_v2 (46-61)
  • list_messages_v2 (66-81)
  • update_message_v2_parts (86-102)
crates/db-user/src/chat_messages_v2_ops.rs (3)
  • create_message_v2 (4-32)
  • list_messages_v2 (34-55)
  • update_message_v2_parts (57-73)
plugins/db/src/commands/chats_v2.rs (2)
crates/db-user/src/chat_conversations_ops.rs (2)
  • create_conversation (4-31)
  • list_conversations (33-54)
crates/db-user/src/chat_messages_v2_ops.rs (3)
  • create_message_v2 (4-32)
  • list_messages_v2 (34-55)
  • update_message_v2_parts (57-73)
crates/db-user/src/sessions_ops.rs (1)
crates/db-core/src/lib.rs (1)
  • conn (17-22)
apps/desktop/src/components/right-panel/hooks/useChat2.ts (2)
apps/desktop/src/hooks/use-license.ts (1)
  • useLicense (9-105)
apps/desktop/src/components/right-panel/utils/chat-transport.ts (1)
  • CustomChatTransport (39-277)
apps/desktop/src/components/right-panel/utils/chat-transport.ts (6)
packages/utils/src/ai.ts (10)
  • UIMessage (25-25)
  • modelProvider (79-86)
  • experimental_createMCPClient (15-15)
  • tool (24-24)
  • dynamicTool (14-14)
  • ChatRequestOptions (11-11)
  • UIMessageChunk (26-26)
  • streamText (23-23)
  • stepCountIs (22-22)
  • smoothStream (21-21)
apps/desktop/src/components/right-panel/utils/mcp-http-wrapper.ts (1)
  • buildVercelToolsFromMcp (5-31)
apps/desktop/src/components/right-panel/utils/tools/search_session_multi_keywords.ts (1)
  • createSearchSessionTool (5-59)
apps/desktop/src/components/right-panel/utils/tools/search_session_date_range.ts (1)
  • createSearchSessionDateRangeTool (5-82)
apps/desktop/src/components/right-panel/utils/tools/edit_enhanced_note.ts (1)
  • createEditEnhancedNoteTool (12-310)
apps/desktop/src/components/right-panel/utils/chat-utils.ts (1)
  • prepareMessagesForAI (82-319)
crates/db-user/src/chat_conversations_ops.rs (2)
plugins/db/src/commands/chats_v2.rs (2)
  • create_conversation (6-21)
  • list_conversations (26-41)
crates/db-core/src/lib.rs (1)
  • conn (17-22)
apps/desktop/src/components/right-panel/components/chat/ui-message.tsx (3)
packages/utils/src/ai.ts (1)
  • UIMessage (25-25)
apps/desktop/src/components/right-panel/utils/markdown-parser.ts (1)
  • parseMarkdownBlocks (8-67)
apps/desktop/src/components/right-panel/components/chat/markdown-card.tsx (1)
  • MarkdownCard (15-140)
crates/db-user/src/chat_messages_v2_ops.rs (2)
plugins/db/src/commands/chats_v2.rs (3)
  • create_message_v2 (46-61)
  • list_messages_v2 (66-81)
  • update_message_v2_parts (86-102)
crates/db-core/src/lib.rs (1)
  • conn (17-22)
apps/desktop/src/components/right-panel/hooks/useChatQueries2.ts (1)
packages/utils/src/ai.ts (1)
  • UIMessage (25-25)
apps/desktop/src/components/right-panel/utils/chat-utils.ts (2)
packages/utils/src/ai.ts (2)
  • UIMessage (25-25)
  • convertToModelMessages (13-13)
apps/desktop/src/contexts/right-panel.tsx (1)
  • SelectionData (10-16)
apps/desktop/src/components/right-panel/views/chat-view.tsx (6)
apps/desktop/src/hooks/use-license.ts (1)
  • useLicense (9-105)
apps/desktop/src/components/right-panel/hooks/useActiveEntity.ts (1)
  • useActiveEntity (14-80)
packages/utils/src/contexts/sessions.tsx (1)
  • useSessions (33-47)
apps/desktop/src/components/right-panel/hooks/useChatQueries2.ts (1)
  • useChatQueries2 (15-175)
apps/desktop/src/components/right-panel/hooks/useChat2.ts (1)
  • useChat2 (17-203)
apps/desktop/src/components/pro-gate-modal/service.ts (1)
  • showProGateModal (6-27)
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (3)
packages/utils/src/ai.ts (1)
  • UIMessage (25-25)
apps/desktop/src/components/right-panel/components/chat/ui-message.tsx (1)
  • UIMessageComponent (154-474)
apps/desktop/src/components/right-panel/components/chat/index.ts (1)
  • UIMessageComponent (8-8)
⏰ 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). (2)
  • GitHub Check: ci (windows, windows-latest)
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (26)
apps/desktop/src/components/right-panel/components/chat/markdown-card.tsx (1)

113-113: Spacing change looks good

Removing external margins makes the card layout parent-controlled. 👍

apps/desktop/src/components/right-panel/utils/tools/edit_enhanced_note.ts (2)

32-39: Softening session-store check is reasonable

Continues execution when sessions map is empty/absent while relying on editor ref. Matches the tool’s needs.


62-67: Confirm HTML sanitization on insertContent

newHtml is inserted directly; ensure TipTap pipeline sanitizes to avoid script/event injection.

Can you confirm the editor’s schema/parse rules sanitize HTML (e.g., via DOMPurify or restricted schema) before insertion?

Also applies to: 180-197, 248-266

plugins/db/src/commands/mod.rs (1)

3-3: Module export looks good

chats_v2 is properly exposed alongside existing modules.

plugins/db/build.rs (1)

50-54: Verified: chat_v2 commands are consistently registered and implemented.

apps/desktop/src/components/right-panel/components/chat/index.ts (1)

8-8: No remaining imports of ChatMessage or MessageContent – verified via ripgrep search across all .ts/.tsx files.

plugins/db/src/lib.rs (1)

73-77: Specta exposure of chat_v2 commands: good.

Matches build.rs and command impls.

crates/db-user/src/chat_messages_v2_types.rs (1)

3-16: Enum wiring looks correct.

Serde/strum mappings match DB values ("system" | "user" | "assistant").

crates/db-user/src/lib.rs (2)

3-10: Modules wired correctly for new chat areas

New modules are declared alongside existing ones; naming and placement look consistent.


33-47: Public re-exports align with module additions

Re-exports are present with #[allow(unused)] to avoid warnings. Looks good.

crates/db-user/src/chat_messages_v2_ops.rs (3)

28-31: Guard against missing RETURNING row (verify)

rows.next().await?.unwrap() assumes the insert always returns a row. This is typically fine with RETURNING *, but please verify libsql guarantees this in all deployments to avoid panics.


40-55: Query shape and ordering look good

Filtering by conversation_id and ordering by created_at ASC matches expected read pattern.


64-69: Timestamp maintenance is correct

Updating updated_at on parts change is correct.

plugins/db/src/commands/chats_v2.rs (5)

3-21: Command: create_conversation mirrors DB API cleanly

Pattern (lock -> get db -> delegate -> map_err) is consistent.


23-41: Command: list_conversations looks good

Consistent with other commands.


43-61: Command: create_message_v2 looks good

Matches DB method and error mapping.


63-81: Command: list_messages_v2 looks good

Ordering left to DB layer; OK.


83-102: Verified command registration update_message_v2_parts is listed in plugins/db/build.rs and exported in plugins/db/src/lib.rs.

crates/db-user/src/chat_conversations_ops.rs (2)

10-31: Insert with RETURNING and row decode looks fine (verify unwrap)

Flow is correct; please confirm RETURNING * always yields one row to avoid unwrap() panic.


56-75: get_conversation API is appropriate

Optional return avoids exceptions for missing rows; matches expected usage.

apps/desktop/src/components/right-panel/hooks/useChat2.ts (1)

95-114: Timestamps on saved assistant messages

You’re using new Date().toISOString() for both timestamps. If the UI has a message timestamp, prefer that to maintain ordering parity with in-memory state. If not, current approach is fine.

apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (1)

119-134: LGTM on UI switch to UIMessageComponent.

Prop plumb-through and keys look correct; layout changes are consistent.

apps/desktop/src/components/right-panel/components/chat/ui-message.tsx (1)

147-149: Confirm HTML sanitization.

Renderer receives HTML from LLM/user sources (via Markdown conversion or metadata.htmlContent). Verify it sanitizes or that upstream does; otherwise XSS is possible.

Also applies to: 187-188

apps/desktop/src/components/right-panel/utils/chat-transport.ts (1)

211-232: LGTM on streaming setup and cleanup path.

stepCountIs, smoothStream, and MCP client teardown on finish look coherent with the new UIMessage stream.

Also applies to: 234-247

apps/desktop/src/components/right-panel/hooks/useChatQueries2.ts (2)

95-101: Confirm UIMessage fields in the external “ai” package and remove redundancy
Please verify whether the imported UIMessage type from "ai" expects both content and parts fields. If it only defines one (or if they’re identical), drop the extra assignment to avoid duplication:

          id: msg.id,
          role: msg.role as "user" | "assistant" | "system",
-          content: parts,
           parts: parts,
           createdAt: new Date(msg.created_at),
           metadata,

43-45: No JSON.parse risk—parts always serialized
All createMessageV2 and updateMessageV2Parts calls use JSON.stringify on parts, so firstUserMessage.parts is guaranteed to be valid JSON.

Comment on lines +75 to +78
transport: transportRef.current,
messages: [],
id: sessionId || "default",
onError: async (err: any) => {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Key chat state by conversation to avoid leakage across conversations

Using only sessionId can merge state between conversations in the same session. Prefer conversationId || sessionId || "default" to isolate streams and message state.

Apply this change:

-    id: sessionId || "default",
+    id: conversationId || sessionId || "default",
📝 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
transport: transportRef.current,
messages: [],
id: sessionId || "default",
onError: async (err: any) => {
transport: transportRef.current,
messages: [],
id: conversationId || sessionId || "default",
onError: async (err: any) => {
🤖 Prompt for AI Agents
In apps/desktop/src/components/right-panel/hooks/useChat2.ts around lines 75 to
78, the chat state is keyed only by sessionId which can cause state leakage
across different conversations in the same session; change the id to use
conversationId || sessionId || "default" (i.e. set id: conversationId ||
sessionId || "default") so streams and message arrays are isolated per
conversation; update any related references or types if necessary to accept
conversationId and ensure transportRef.current and onError remain unchanged.

Comment on lines +55 to +57
const latestConversation = conversationsQuery.data.sort((a, b) =>
b.mostRecentTimestamp - a.mostRecentTimestamp
)[0];
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid mutating React Query cache when selecting the latest conversation.

Sorting the cached array in place can cause subtle cache corruption. Compute the latest item without mutation.

-      const latestConversation = conversationsQuery.data.sort((a, b) =>
-        b.mostRecentTimestamp - a.mostRecentTimestamp
-      )[0];
+      const latestConversation = conversationsQuery.data.reduce(
+        (latest, c) => (c.mostRecentTimestamp > latest.mostRecentTimestamp ? c : latest),
+        conversationsQuery.data[0],
+      );
📝 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 latestConversation = conversationsQuery.data.sort((a, b) =>
b.mostRecentTimestamp - a.mostRecentTimestamp
)[0];
const latestConversation = conversationsQuery.data.reduce(
(latest, c) => (c.mostRecentTimestamp > latest.mostRecentTimestamp ? c : latest),
conversationsQuery.data[0],
);
🤖 Prompt for AI Agents
In apps/desktop/src/components/right-panel/hooks/useChatQueries2.ts around lines
55 to 57, the code sorts conversationsQuery.data in place which mutates the
React Query cache; change this to a non-mutating selection: either compute a
shallow copy before sorting (e.g., use .slice() then .sort()) or, better, derive
the latest conversation with a single-pass reducer or Math.max style comparison
to pick the item with the largest mostRecentTimestamp, and assign that result to
latestConversation so the original cache array is never mutated.

Comment on lines 77 to 103
useEffect(() => {
const loadMessages = async () => {
if (currentConversationId) {
try {
const { commands } = await import("@hypr/plugin-db");
const dbMessages = await commands.listMessagesV2(currentConversationId);

const uiMessages = dbMessages.map(msg => ({
id: msg.id,
role: msg.role as "user" | "assistant" | "system",
parts: JSON.parse(msg.parts),
metadata: msg.metadata ? JSON.parse(msg.metadata) : {},
}));

setMessages(uiMessages);
} catch (error) {
console.error("Failed to load messages:", error);
}
} else {
setMessages([]);
console.log("Cleared messages for new conversation");
}
};

loadMessages();
}, [currentConversationId, setMessages]);

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove duplicate DB fetch; use the hook’s messages instead.

This effect re-implements listMessagesV2 and JSON parsing that already exists in useChatQueries2, causing double I/O and divergent shaping.

-  useEffect(() => {
-    const loadMessages = async () => {
-      if (currentConversationId) {
-        try {
-          const { commands } = await import("@hypr/plugin-db");
-          const dbMessages = await commands.listMessagesV2(currentConversationId);
-
-          const uiMessages = dbMessages.map(msg => ({
-            id: msg.id,
-            role: msg.role as "user" | "assistant" | "system",
-            parts: JSON.parse(msg.parts),
-            metadata: msg.metadata ? JSON.parse(msg.metadata) : {},
-          }));
-
-          setMessages(uiMessages);
-        } catch (error) {
-          console.error("Failed to load messages:", error);
-        }
-      } else {
-        setMessages([]);
-        console.log("Cleared messages for new conversation");
-      }
-    };
-
-    loadMessages();
-  }, [currentConversationId, setMessages]);
+  // Sync messages from useChatQueries2 into the chat state
+  useEffect(() => {
+    if (dbMessages && !isGenerating) {
+      setMessages(dbMessages);
+    }
+  }, [dbMessages, isGenerating, setMessages]);

Note: See the companion diff below to expose dbMessages from useChatQueries2.

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

🤖 Prompt for AI Agents
In apps/desktop/src/components/right-panel/views/chat-view.tsx around lines 77
to 103, the useEffect duplicates DB fetch and JSON parsing that useChatQueries2
already performs; remove the dynamic import of "@hypr/plugin-db" and the
listMessagesV2 call and instead consume the dbMessages (or the already-shaped
messages) exposed by useChatQueries2—setMessages directly from that hook’s
output (or map its shaped output if necessary), remove local JSON.parse usage,
and keep the else branch to clear messages when no conversation id is present so
we avoid double I/O and ensure a single source of truth for message shaping.

Comment on lines +18 to +24
message.id,
message.conversation_id,
message.role.to_string(),
message.parts,
message.metadata.unwrap_or_default(),
message.created_at.to_rfc3339(),
message.updated_at.to_rfc3339(),
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Default metadata to valid JSON

Persisting empty string for metadata can break JSON parsing downstream. Prefer "{}" as a safe default.

Apply this change:

-                    message.metadata.unwrap_or_default(),
+                    message.metadata.unwrap_or_else(|| "{}".to_string()),
🤖 Prompt for AI Agents
In crates/db-user/src/chat_messages_v2_ops.rs around lines 18–24, the code
currently persists message.metadata.unwrap_or_default(), which can produce an
empty string and break downstream JSON parsing; change the default to a valid
JSON object string (e.g. "{}", using unwrap_or_else or equivalent) so stored
metadata is never an empty string but a safe "{}" JSON value.

Comment on lines +8 to +9
export { useChat } from "@ai-sdk/react";

Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Re-exporting useChat: OK.

Surface aligns with the new UIMessage flow.

Verify dependency presence/versions:


🏁 Script executed:

#!/bin/bash
# Confirm packages and lock entries exist for ai and @ai-sdk/react.
rg -n "\"ai\"|\"@ai-sdk/react\"" package.json pnpm-lock.yaml yarn.lock package-lock.json -S

Length of output: 153


🏁 Script executed:

#!/bin/bash
# Check direct dependency in package.json
echo "Checking @ai-sdk/react in package.json dependencies:"
jq '.dependencies["@ai-sdk/react"] // .devDependencies["@ai-sdk/react"]' package.json

echo "Checking ai in package.json dependencies:"
jq '.dependencies.ai // .devDependencies.ai' package.json

# Confirm pnpm lockfile exists and contains @ai-sdk/react
echo "Inspecting pnpm-lock.yaml for @ai-sdk/react:"
ls -l pnpm-lock.yaml
rg -n '"@ai-sdk/react"' pnpm-lock.yaml -n -C3 || echo "No entry found for @ai-sdk/react in pnpm-lock.yaml"

Length of output: 271


Re-exporting useChat is correct, but lockfile needs updating.

  • package.json lists "@ai-sdk/react": "^2.0.30" but pnpm-lock.yaml contains no entry for it.
  • Run pnpm install (or pnpm up @ai-sdk/react) and commit the updated pnpm-lock.yaml so the dependency is properly locked.
🤖 Prompt for AI Agents
In packages/utils/src/ai.ts around lines 8 to 9, the file correctly re-exports
useChat from "@ai-sdk/react" but the repository lockfile is missing that package
entry; run `pnpm install` (or `pnpm up @ai-sdk/react`) to update pnpm-lock.yaml
so the dependency is recorded, then add and commit the updated pnpm-lock.yaml to
the branch.

Comment on lines +11 to +13
type ChatRequestOptions,
type ChatTransport,
convertToModelMessages,
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

New type exports: confirm compatibility with installed ai version.

Ensure these symbols exist in the pinned ai version to avoid type export breakages.

Also applies to: 19-19, 25-27


🏁 Script executed:

#!/bin/bash
# Quick check: list 'ai' version and grep for expected exports in installed d.ts (if present).
rg -n "\"ai\"" package.json
fd -a "index.d.ts" node_modules/ai | xargs -I{} rg -n "convertToModelMessages|ChatTransport|UIMessageChunk|UIMessage|LanguageModel" {}

Length of output: 102


🏁 Script executed:

#!/bin/bash
# Extract the pinned ai version from package.json
version=$(grep -Po '"ai"\s*:\s*"\K[^"]+' package.json)
version=${version#^}
echo "AI version: $version"
# Download the corresponding index.d.ts from unpkg and verify expected exports
curl -sL https://unpkg.com/ai@"$version"/index.d.ts \
  | grep -E "convertToModelMessages|ChatTransport|UIMessageChunk|UIMessage|LanguageModel|ChatRequestOptions"

Length of output: 12


🏁 Script executed:

#!/bin/bash
# Locate ai.ts and inspect its import statements
file=$(fd --type f ai.ts)
echo "ai.ts path: $file"
awk 'NR<=50' "$file"

Length of output: 1454


🏁 Script executed:

#!/bin/bash
# Show dependencies section from package.json to locate 'ai' entry
start_line=$(grep -n '"dependencies"' -n package.json | head -1 | cut -d: -f1)
end_line=$((start_line + 50))
sed -n "${start_line},${end_line}p" package.json

Length of output: 56


Add missing “ai” dependency and pin to a compatible version
packages/utils/src/ai.ts imports and re-exports symbols from “ai” (e.g. ChatRequestOptions, ChatTransport, convertToModelMessages, UIMessage, UIMessageChunk, LanguageModel), but package.json has no “ai” entry—this will break at publish/install. Add “ai” to dependencies (pin to a version that exports all these symbols).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (6)
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (2)

112-112: Effect deps LGTM; stale isError fixed.

Including isError resolves the stale read noted earlier.


55-57: Don’t show “Thinking” on error.

Early return ignores isError, so indicator persists after failures.

-    if (isSubmitted) {
-      return true;
-    }
+    if (isError) {
+      return false;
+    }
+    if (isSubmitted) {
+      return true;
+    }
apps/desktop/src/components/right-panel/utils/chat-transport.ts (1)

177-178: progress_update tool missing execute() — runtime failure risk.

tool({ inputSchema }) without execute will be unusable/throw.

-      ...(type === "HyprLocal" && { progress_update: tool({ inputSchema: z.any() }) }),
+      ...(type === "HyprLocal" && {
+        progress_update: tool({
+          description: "Internal progress updates from local runtime. Not for direct invocation.",
+          inputSchema: z.any(),
+          execute: async () => ({ ok: true }),
+        }),
+      }),
apps/desktop/src/components/right-panel/views/chat-view.tsx (2)

77-101: Remove duplicate DB fetch; source messages from useChatQueries2.

This reimplements listMessagesV2/JSON parsing already handled by the hook, causing double I/O and divergent shaping.

-  useEffect(() => {
-    const loadMessages = async () => {
-      if (currentConversationId) {
-        try {
-          const { commands } = await import("@hypr/plugin-db");
-          const dbMessages = await commands.listMessagesV2(currentConversationId);
-
-          const uiMessages = dbMessages.map(msg => ({
-            id: msg.id,
-            role: msg.role as "user" | "assistant" | "system",
-            parts: JSON.parse(msg.parts),
-            metadata: msg.metadata ? JSON.parse(msg.metadata) : {},
-          }));
-
-          setMessages(uiMessages);
-        } catch (error) {
-          console.error("Failed to load messages:", error);
-        }
-      } else {
-        setMessages([]);
-      }
-    };
-
-    loadMessages();
-  }, [currentConversationId, setMessages]);
+  // Sync DB-shaped messages into chat state when idle (avoid clobbering stream state)
+  useEffect(() => {
+    if (dbMessages && !isGenerating) {
+      setMessages(dbMessages);
+    }
+  }, [dbMessages, isGenerating, setMessages]);

45-50: Expose messages from useChatQueries2 for the sync.

Thread hook-provided messages into the component.

   const {
     conversations,
     sessionData,
     createConversation,
     getOrCreateConversationId,
+    messages: dbMessages,
   } = useChatQueries2({
apps/desktop/src/components/right-panel/utils/chat-utils.ts (1)

141-146: Resolved: closing parenthesis added to eventInfo template

The ternary/template literal is now balanced and compiles.

🧹 Nitpick comments (8)
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (2)

51-51: Timer ref type: prefer ReturnType.

Avoid NodeJS.Timeout in browser/tauri DOM builds.

-  const thinkingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
+  const thinkingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

16-43: Optional: avoid injecting <style> per render.

Minor: move CSS to a static stylesheet or memoize the component to reduce DOM churn. Why: fewer style nodes when toggling indicator frequently.

apps/desktop/src/components/right-panel/utils/chat-transport.ts (1)

125-131: Avoid name shadowing “tool” import.

Loop variable ‘tool’ shadows the imported tool() helper; rename for clarity.

-        for (const [toolName, tool] of Object.entries(tools as Record<string, any>)) {
-          newMcpTools[toolName] = dynamicTool({
-            description: tool.description,
-            inputSchema: tool.inputSchema || z.any(),
-            execute: tool.execute,
-          });
+        for (const [toolName, srvTool] of Object.entries(tools as Record<string, any>)) {
+          newMcpTools[toolName] = dynamicTool({
+            description: srvTool.description,
+            inputSchema: srvTool.inputSchema || z.any(),
+            execute: srvTool.execute,
+          });
         }
apps/desktop/src/components/right-panel/utils/chat-utils.ts (5)

37-76: Tighten tool-part filtering; avoid 'any' variable and long OR chain

Use a Set and a simple state guard to reduce noise and accidental state drift.

Apply within this function:

 export const cleanUIMessages = (messages: UIMessage[]): UIMessage[] => {
   return messages.map(message => {
@@
-    const cleanedParts = message.parts.filter(part => {
+    const cleanedParts = message.parts.filter(part => {
+      const disallowedStates = new Set([
+        "input-available",
+        "output-available",
+        "input-streaming",
+        "output-error",
+      ]);
       // Check if this is a tool part (dynamic-tool or tool-*)
       if (part.type === "dynamic-tool" || part.type?.startsWith("tool-")) {
-        const toolPart = part as any;
-
-        // Filter out UI-specific states that cause conversion errors
-        // Keep only text parts and tool parts without problematic states
-        if (
-          toolPart.state === "input-available"
-          || toolPart.state === "output-available"
-          || toolPart.state === "input-streaming"
-          || toolPart.state === "output-error"
-        ) {
-          return false; // Remove these tool parts
-        }
+        // Filter out UI-only states that break conversion
+        if ("state" in part && disallowedStates.has((part as any).state)) {
+          return false;
+        }
       }
 
       // Keep all other parts (text, etc.)
       return true;
     });

82-92: Add explicit return type derived from the converter to avoid any[]

Keeps the function and accumulator precisely typed without extra imports.

-export const prepareMessagesForAI = async (
+export const prepareMessagesForAI = async (
   messages: UIMessage[],
   options: {
@@
-  },
-) => {
+  },
+): Promise<ReturnType<typeof convertToModelMessages>> => {
@@
-  const preparedMessages: any[] = [];
+  const preparedMessages: ReturnType<typeof convertToModelMessages> = [];

Also applies to: 190-190


149-157: Normalize modelId for toolEnabled check to reduce brittle matches

This handles vendor prefixes (e.g., openai/) and casing in one place.

-  const toolEnabled = !!(
-    modelId === "gpt-4.1"
-    || modelId === "openai/gpt-4.1"
-    || modelId === "anthropic/claude-sonnet-4"
-    || modelId === "openai/gpt-4o"
-    || modelId === "gpt-4o"
-    || modelId === "openai/gpt-5"
-    || (apiBase && apiBase.includes("pro.hyprnote.com"))
-  );
+  const normalizedId = String(modelId).toLowerCase();
+  const toolEnabled =
+    ["gpt-4.1", "gpt-4o", "gpt-5", "claude-sonnet-4"].some(k => normalizedId.includes(k)) ||
+    (apiBase?.includes("pro.hyprnote.com") ?? false);

213-227: Rename inner sessionData to avoid shadowing outer session context

Prevents confusion between the current note vs. the conversation/session data.

-              const sessionData = await dbCommands.getSession({ id: mention.id });
-              if (sessionData) {
+              const noteSession = await dbCommands.getSession({ id: mention.id });
+              if (noteSession) {
                 let noteContent = "";
-                if (sessionData.enhanced_memo_html && sessionData.enhanced_memo_html.trim() !== "") {
-                  noteContent = sessionData.enhanced_memo_html;
-                } else if (sessionData.raw_memo_html && sessionData.raw_memo_html.trim() !== "") {
-                  noteContent = sessionData.raw_memo_html;
+                if (noteSession.enhanced_memo_html && noteSession.enhanced_memo_html.trim() !== "") {
+                  noteContent = noteSession.enhanced_memo_html;
+                } else if (noteSession.raw_memo_html && noteSession.raw_memo_html.trim() !== "") {
+                  noteContent = noteSession.raw_memo_html;
                 } else {
                   continue;
                 }

235-239: Prevent "undefined" literals in the human profile block

Nullish-coalesce optional fields to empty strings.

-                humanContent += "Name: " + humanData?.full_name + "\n";
-                humanContent += "Email: " + humanData?.email + "\n";
-                humanContent += "Job Title: " + humanData?.job_title + "\n";
-                humanContent += "LinkedIn: " + humanData?.linkedin_username + "\n";
+                humanContent += "Name: " + (humanData?.full_name ?? "") + "\n";
+                humanContent += "Email: " + (humanData?.email ?? "") + "\n";
+                humanContent += "Job Title: " + (humanData?.job_title ?? "") + "\n";
+                humanContent += "LinkedIn: " + (humanData?.linkedin_username ?? "") + "\n";
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3663170 and 77d2743.

📒 Files selected for processing (7)
  • apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (4 hunks)
  • apps/desktop/src/components/right-panel/utils/chat-transport.ts (1 hunks)
  • apps/desktop/src/components/right-panel/utils/chat-utils.ts (5 hunks)
  • apps/desktop/src/components/right-panel/views/chat-view.tsx (6 hunks)
  • crates/db-user/src/chat_conversations_migration.sql (1 hunks)
  • crates/db-user/src/chat_messages_v2_migration.sql (1 hunks)
  • crates/db-user/src/sessions_ops.rs (0 hunks)
💤 Files with no reviewable changes (1)
  • crates/db-user/src/sessions_ops.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/db-user/src/chat_messages_v2_migration.sql
  • crates/db-user/src/chat_conversations_migration.sql
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

⚙️ CodeRabbit configuration file

**/*.{js,ts,tsx,rs}: 1. Do not add any error handling. Keep the existing one.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".

Files:

  • apps/desktop/src/components/right-panel/utils/chat-transport.ts
  • apps/desktop/src/components/right-panel/utils/chat-utils.ts
  • apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx
  • apps/desktop/src/components/right-panel/views/chat-view.tsx
🧬 Code graph analysis (4)
apps/desktop/src/components/right-panel/utils/chat-transport.ts (6)
packages/utils/src/ai.ts (11)
  • ChatTransport (12-12)
  • UIMessage (25-25)
  • modelProvider (79-86)
  • experimental_createMCPClient (15-15)
  • tool (24-24)
  • dynamicTool (14-14)
  • ChatRequestOptions (11-11)
  • UIMessageChunk (26-26)
  • streamText (23-23)
  • stepCountIs (22-22)
  • smoothStream (21-21)
apps/desktop/src/components/right-panel/utils/mcp-http-wrapper.ts (1)
  • buildVercelToolsFromMcp (5-31)
apps/desktop/src/components/right-panel/utils/tools/search_session_multi_keywords.ts (1)
  • createSearchSessionTool (5-59)
apps/desktop/src/components/right-panel/utils/tools/search_session_date_range.ts (1)
  • createSearchSessionDateRangeTool (5-82)
apps/desktop/src/components/right-panel/utils/tools/edit_enhanced_note.ts (1)
  • createEditEnhancedNoteTool (12-310)
apps/desktop/src/components/right-panel/utils/chat-utils.ts (1)
  • prepareMessagesForAI (82-319)
apps/desktop/src/components/right-panel/utils/chat-utils.ts (2)
packages/utils/src/ai.ts (2)
  • UIMessage (25-25)
  • convertToModelMessages (13-13)
apps/desktop/src/contexts/right-panel.tsx (1)
  • SelectionData (10-16)
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (2)
packages/utils/src/ai.ts (1)
  • UIMessage (25-25)
apps/desktop/src/components/right-panel/components/chat/ui-message.tsx (1)
  • UIMessageComponent (154-474)
apps/desktop/src/components/right-panel/views/chat-view.tsx (8)
apps/desktop/src/contexts/right-panel.tsx (1)
  • useRightPanel (230-236)
apps/desktop/src/hooks/use-license.ts (1)
  • useLicense (9-105)
apps/desktop/src/components/right-panel/components/chat/types.ts (1)
  • ChatSession (17-22)
apps/desktop/src/components/right-panel/hooks/useActiveEntity.ts (1)
  • useActiveEntity (14-80)
packages/utils/src/contexts/sessions.tsx (1)
  • useSessions (33-47)
apps/desktop/src/components/right-panel/hooks/useChatQueries2.ts (1)
  • useChatQueries2 (15-175)
apps/desktop/src/components/right-panel/hooks/useChat2.ts (1)
  • useChat2 (17-203)
apps/desktop/src/components/pro-gate-modal/service.ts (1)
  • showProGateModal (6-27)
⏰ 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). (2)
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (6)
apps/desktop/src/components/right-panel/utils/chat-transport.ts (1)

66-73: Tool-gating predicates aligned across loadMCPTools/getTools.

Nice—HyprLocal isn’t special-cased in one and omitted in the other anymore, removing earlier mismatch.

Also applies to: 150-157

apps/desktop/src/components/right-panel/utils/chat-utils.ts (5)

5-7: UIMessage + converter import switch looks correct

Matches the v5 UIMessage pipeline and is used below.


159-168: MCP tools mapping is fine

Uses enabled servers only and a stable shape for the system prompt.


185-191: Clean-then-convert sequencing is appropriate

Prevents SDK v5 conversion errors from UI-only tool states.


192-197: Avoid duplicate system messages Confirm that convertToModelMessages never emits a message with role: "system" before you unconditionally inject one—otherwise filter or remove it to prevent duplicates.
Location: apps/desktop/src/components/right-panel/utils/chat-utils.ts lines 192–197 & 312–315


288-297: Confirm non-text parts aren’t dropped
In apps/desktop/src/components/right-panel/utils/chat-utils.ts (lines 288–297), you extract only .text from each msg.content part. Since convertToModelMessages is re-exported from the external “ai” package, its output shape isn’t in this repo—please verify whether it can emit non-text parts that need preserving and confirm dropping them here is intentional.

Comment on lines +49 to +53
const provider = await modelProvider();
const model = provider.languageModel("defaultModel");

return model;
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify modelProvider API shape (“languageModel” vs “languageModels”).

Ensure provider.languageModel("defaultModel") is valid; some providers expose provider.languageModels.defaultModel.


🏁 Script executed:

#!/bin/bash
# Inspect provider API usage to confirm correct accessor.
rg -n -C2 'languageModel\\(' packages/utils/src
rg -n -C2 '\\blanguageModels\\b' packages/utils/src
rg -n -C2 'modelProvider\\(' packages/utils/src

Length of output: 165


🏁 Script executed:

#!/bin/bash
# Retry with corrected regex for verifying provider API shape.
rg -n -C2 'languageModel\(' packages/utils/src
rg -n -C2 'languageModels' packages/utils/src
rg -n -C2 'modelProvider' packages/utils/src

Length of output: 543


Use the correct languageModels accessor
modelProvider returns an object with a languageModels map, not a languageModel() method. Replace:

- const model = provider.languageModel("defaultModel");
+ const model = provider.languageModels.defaultModel;

in apps/desktop/src/components/right-panel/utils/chat-transport.ts (around lines 49–53).

📝 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 provider = await modelProvider();
const model = provider.languageModel("defaultModel");
return model;
}
const provider = await modelProvider();
const model = provider.languageModels.defaultModel;
return model;
}
🤖 Prompt for AI Agents
In apps/desktop/src/components/right-panel/utils/chat-transport.ts around lines
49–53, replace the incorrect call to provider.languageModel("defaultModel") with
the proper accessor for the languageModels map: retrieve the model via
provider.languageModels.get?.("defaultModel") ??
provider.languageModels["defaultModel"], assign that to model, and add a simple
null check to throw or log a clear error if the model is not found.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant