-
Notifications
You must be signed in to change notification settings - Fork 351
Chat refactor new db final (with use chat) #1456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughReplaces 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
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
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
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Pre-merge checks (2 warnings, 1 inconclusive)❌ Failed checks (2 warnings, 1 inconclusive)
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.
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 unit tests
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. Comment |
There was a problem hiding this 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 marksCurrent 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 propIt 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 includetype === "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 entryAvoid accidental pattern mismatches in .gitignore by removing the trailing space.
-CLAUDE.md +CLAUDE.mdapps/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 depsRefs 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 entriesLength 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 pathTo 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 recencyOrdering 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
usessessions
andonError
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 byid
; the extraconversationId
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. PreferReturnType<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 removeany
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: narrowallMcpClients
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
⛔ 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 goodRemoving 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 reasonableContinues execution when sessions map is empty/absent while relying on editor ref. Matches the tool’s needs.
62-67
: Confirm HTML sanitization on insertContentnewHtml 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 goodchats_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 areasNew modules are declared alongside existing ones; naming and placement look consistent.
33-47
: Public re-exports align with module additionsRe-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 withRETURNING *
, but please verify libsql guarantees this in all deployments to avoid panics.
40-55
: Query shape and ordering look goodFiltering by conversation_id and ordering by created_at ASC matches expected read pattern.
64-69
: Timestamp maintenance is correctUpdating
updated_at
on parts change is correct.plugins/db/src/commands/chats_v2.rs (5)
3-21
: Command: create_conversation mirrors DB API cleanlyPattern (lock -> get db -> delegate -> map_err) is consistent.
23-41
: Command: list_conversations looks goodConsistent with other commands.
43-61
: Command: create_message_v2 looks goodMatches DB method and error mapping.
63-81
: Command: list_messages_v2 looks goodOrdering 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 avoidunwrap()
panic.
56-75
: get_conversation API is appropriateOptional 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 messagesYou’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 toUIMessageComponent
.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 ormetadata.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 importedUIMessage
type from"ai"
expects bothcontent
andparts
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
AllcreateMessageV2
andupdateMessageV2Parts
calls useJSON.stringify
onparts
, sofirstUserMessage.parts
is guaranteed to be valid JSON.
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx
Show resolved
Hide resolved
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx
Outdated
Show resolved
Hide resolved
transport: transportRef.current, | ||
messages: [], | ||
id: sessionId || "default", | ||
onError: async (err: any) => { |
There was a problem hiding this comment.
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.
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.
const latestConversation = conversationsQuery.data.sort((a, b) => | ||
b.mostRecentTimestamp - a.mostRecentTimestamp | ||
)[0]; |
There was a problem hiding this comment.
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.
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.
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]); | ||
|
There was a problem hiding this comment.
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.
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(), |
There was a problem hiding this comment.
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.
export { useChat } from "@ai-sdk/react"; | ||
|
There was a problem hiding this comment.
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
(orpnpm 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.
type ChatRequestOptions, | ||
type ChatTransport, | ||
convertToModelMessages, |
There was a problem hiding this comment.
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).
There was a problem hiding this 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 templateThe 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 chainUse 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 matchesThis 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 contextPrevents 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 blockNullish-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
📒 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 correctMatches the v5 UIMessage pipeline and is used below.
159-168
: MCP tools mapping is fineUses enabled servers only and a stable shape for the system prompt.
185-191
: Clean-then-convert sequencing is appropriatePrevents SDK v5 conversion errors from UI-only tool states.
192-197
: Avoid duplicate system messages Confirm thatconvertToModelMessages
never emits a message withrole: "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 eachmsg.content
part. SinceconvertToModelMessages
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.
const provider = await modelProvider(); | ||
const model = provider.languageModel("defaultModel"); | ||
|
||
return model; | ||
} |
There was a problem hiding this comment.
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.
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.
No description provided.