diff --git a/apps/desktop/src/components/right-panel/components/chat/message-content.tsx b/apps/desktop/src/components/right-panel/components/chat/message-content.tsx
index 6d7acb4547..d752738a0b 100644
--- a/apps/desktop/src/components/right-panel/components/chat/message-content.tsx
+++ b/apps/desktop/src/components/right-panel/components/chat/message-content.tsx
@@ -1,5 +1,6 @@
import { commands as miscCommands } from "@hypr/plugin-misc";
import Renderer from "@hypr/tiptap/renderer";
+import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@hypr/ui/components/ui/accordion";
import { PencilRuler } from "lucide-react";
import { useEffect, useState } from "react";
import { MarkdownCard } from "./markdown-card";
@@ -12,6 +13,52 @@ interface MessageContentProps {
onApplyMarkdown?: (markdownContent: string) => void;
}
+function ToolDetailsRenderer({ details }: { details: any }) {
+ if (!details) {
+ return (
+
+ No details available...
+
+ );
+ }
+
+ return (
+
+
+ {typeof details === 'object' ? JSON.stringify(details, null, 2) : String(details)}
+
+
+ );
+}
+
function MarkdownText({ content }: { content: string }) {
const [htmlContent, setHtmlContent] = useState("");
@@ -124,31 +171,71 @@ function MarkdownText({ content }: { content: string }) {
export function MessageContent({ message, sessionTitle, hasEnhancedNote, onApplyMarkdown }: MessageContentProps) {
if (message.type === "tool-start") {
- return (
-
+ const hasToolDetails = message.toolDetails;
+
+ if (hasToolDetails) {
+ return (
-
-
- Called tool: {message.content}
-
+
+
+
+
+
+
+ Called tool: {message.content}
+
+
+
+
+
+
+
+
-
- );
+ );
+ } else {
+ return (
+
+
+
+
+ Called tool: {message.content}
+
+
+
+ );
+ }
}
if (message.type === "tool-result") {
diff --git a/apps/desktop/src/components/right-panel/components/chat/types.ts b/apps/desktop/src/components/right-panel/components/chat/types.ts
index 21b4d7c7fb..cd3a9cca64 100644
--- a/apps/desktop/src/components/right-panel/components/chat/types.ts
+++ b/apps/desktop/src/components/right-panel/components/chat/types.ts
@@ -11,6 +11,7 @@ export interface Message {
isUser: boolean;
timestamp: Date;
type: "text-delta" | "tool-start" | "tool-result" | "tool-error" | "generating";
+ toolDetails?: any;
}
export type ChatSession = {
diff --git a/apps/desktop/src/components/right-panel/hooks/useChatLogic.ts b/apps/desktop/src/components/right-panel/hooks/useChatLogic.ts
index 35a298fca9..8ed740b08e 100644
--- a/apps/desktop/src/components/right-panel/hooks/useChatLogic.ts
+++ b/apps/desktop/src/components/right-panel/hooks/useChatLogic.ts
@@ -160,6 +160,7 @@ export function useChatLogic({
role: "User",
content: userMessage.content.trim(),
type: "text-delta",
+ tool_details: null,
});
const aiMessageId = crypto.randomUUID();
@@ -340,7 +341,7 @@ export function useChatLogic({
userId,
apiBase,
),
- stopWhen: stepCountIs(3),
+ stopWhen: stepCountIs(5),
tools: {
...(type === "HyprLocal" && { update_progress: tool({ inputSchema: z.any() }) }),
...(shouldUseTools && { ...newMcpTools, search_sessions_multi_keywords: searchTool, ...hyprMcpTools }),
@@ -428,6 +429,7 @@ export function useChatLogic({
role: "Assistant",
type: "text-delta",
content: aiResponse.trim(),
+ tool_details: null,
});
} catch (error) {
console.error("Failed to save AI text:", error);
@@ -445,6 +447,7 @@ export function useChatLogic({
isUser: false,
timestamp: new Date(),
type: "tool-start",
+ toolDetails: chunk.input,
};
setMessages((prev) => [...prev, toolStartMessage]);
@@ -456,6 +459,7 @@ export function useChatLogic({
role: "Assistant",
content: toolStartMessage.content,
type: "tool-start",
+ tool_details: JSON.stringify(chunk.input),
});
}
@@ -479,6 +483,7 @@ export function useChatLogic({
role: "Assistant",
content: toolResultMessage.content,
type: "tool-result",
+ tool_details: null,
});
}
@@ -500,6 +505,7 @@ export function useChatLogic({
role: "Assistant",
content: toolErrorMessage.content,
type: "tool-error",
+ tool_details: null,
});
}
@@ -514,6 +520,7 @@ export function useChatLogic({
role: "Assistant",
type: "text-delta",
content: aiResponse.trim(),
+ tool_details: null,
});
}
@@ -570,6 +577,7 @@ export function useChatLogic({
role: "Assistant",
content: finalErrorMessage,
type: "text-delta",
+ tool_details: null,
});
}
};
diff --git a/apps/desktop/src/components/right-panel/hooks/useChatQueries.ts b/apps/desktop/src/components/right-panel/hooks/useChatQueries.ts
index 38ef252506..636a741902 100644
--- a/apps/desktop/src/components/right-panel/hooks/useChatQueries.ts
+++ b/apps/desktop/src/components/right-panel/hooks/useChatQueries.ts
@@ -86,6 +86,16 @@ export function useChatQueries({
timestamp: new Date(msg.created_at),
type: msg.type || "text-delta",
parts: msg.role === "Assistant" ? parseMarkdownBlocks(msg.content) : undefined,
+ toolDetails: msg.type === "tool-start" && msg.tool_details
+ ? (() => {
+ try {
+ return JSON.parse(msg.tool_details);
+ } catch (error) {
+ console.error("Failed to parse tool_details for tool-start:", msg.id, error);
+ return undefined;
+ }
+ })()
+ : undefined,
}));
},
});
diff --git a/crates/db-user/src/chat_messages_migration_2.sql b/crates/db-user/src/chat_messages_migration_2.sql
new file mode 100644
index 0000000000..3f30423c84
--- /dev/null
+++ b/crates/db-user/src/chat_messages_migration_2.sql
@@ -0,0 +1,4 @@
+ALTER TABLE
+ chat_messages
+ADD
+ COLUMN tool_details TEXT DEFAULT NULL;
diff --git a/crates/db-user/src/chat_messages_ops.rs b/crates/db-user/src/chat_messages_ops.rs
index 560eda9a68..569f4a5407 100644
--- a/crates/db-user/src/chat_messages_ops.rs
+++ b/crates/db-user/src/chat_messages_ops.rs
@@ -15,8 +15,9 @@ impl UserDatabase {
created_at,
role,
content,
- type
- ) VALUES (?, ?, ?, ?, ?, ?)
+ type,
+ tool_details
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING *",
vec![
message.id,
@@ -25,6 +26,7 @@ impl UserDatabase {
message.role.to_string(),
message.content,
message.r#type.to_string(),
+ message.tool_details.unwrap_or_default(),
],
)
.await?;
diff --git a/crates/db-user/src/chat_messages_types.rs b/crates/db-user/src/chat_messages_types.rs
index 1665fda4ea..a8a1eb7a3e 100644
--- a/crates/db-user/src/chat_messages_types.rs
+++ b/crates/db-user/src/chat_messages_types.rs
@@ -34,5 +34,6 @@ user_common_derives! {
pub role: ChatMessageRole,
pub content: String,
pub r#type: ChatMessageType,
+ pub tool_details: Option,
}
}
diff --git a/crates/db-user/src/init.rs b/crates/db-user/src/init.rs
index c797772a9c..5210d107de 100644
--- a/crates/db-user/src/init.rs
+++ b/crates/db-user/src/init.rs
@@ -785,6 +785,7 @@ pub async fn seed(db: &UserDatabase, user_id: impl Into) -> Result<(), c
content: "Hello, how are you?".to_string(),
created_at: now,
r#type: ChatMessageType::TextDelta,
+ tool_details: None,
};
let _ = db.upsert_chat_message(chat_message_1).await?;
diff --git a/crates/db-user/src/lib.rs b/crates/db-user/src/lib.rs
index abb73c2056..01aa7d722c 100644
--- a/crates/db-user/src/lib.rs
+++ b/crates/db-user/src/lib.rs
@@ -129,7 +129,7 @@ impl std::ops::Deref for UserDatabase {
}
// Append only. Do not reorder.
-const MIGRATIONS: [&str; 23] = [
+const MIGRATIONS: [&str; 24] = [
include_str!("./calendars_migration.sql"),
include_str!("./configs_migration.sql"),
include_str!("./events_migration.sql"),
@@ -153,6 +153,7 @@ const MIGRATIONS: [&str; 23] = [
include_str!("./session_participants_migration_1.sql"),
include_str!("./events_migration_2.sql"),
include_str!("./chat_messages_migration_1.sql"),
+ include_str!("./chat_messages_migration_2.sql"),
];
pub async fn migrate(db: &UserDatabase) -> Result<(), crate::Error> {
diff --git a/plugins/db/js/bindings.gen.ts b/plugins/db/js/bindings.gen.ts
index e38f4ee566..11df378f0f 100644
--- a/plugins/db/js/bindings.gen.ts
+++ b/plugins/db/js/bindings.gen.ts
@@ -162,7 +162,7 @@ async deleteTag(tagId: string) : Promise {
export type Calendar = { id: string; tracking_id: string; user_id: string; platform: Platform; name: string; selected: boolean; source: string | null }
export type ChatGroup = { id: string; user_id: string; name: string | null; created_at: string; session_id: string }
-export type ChatMessage = { id: string; group_id: string; created_at: string; role: ChatMessageRole; content: string; type: ChatMessageType }
+export type ChatMessage = { id: string; group_id: string; created_at: string; role: ChatMessageRole; content: string; type: ChatMessageType; tool_details: string | null }
export type ChatMessageRole = "User" | "Assistant"
export type ChatMessageType = "text-delta" | "tool-start" | "tool-result" | "tool-error"
export type Config = { id: string; user_id: string; general: ConfigGeneral; notification: ConfigNotification; ai: ConfigAI }