diff --git a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx index 23c640ceb..f569940d7 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx @@ -37,55 +37,11 @@ import { Thread } from "@/components/assistant-ui/thread"; import { MobileEditorPanel } from "@/components/editor-panel/editor-panel"; import { MobileHitlEditPanel } from "@/components/hitl-edit-panel/hitl-edit-panel"; import { MobileReportPanel } from "@/components/report-panel/report-panel"; -import { - CreateConfluencePageToolUI, - DeleteConfluencePageToolUI, - UpdateConfluencePageToolUI, -} from "@/components/tool-ui/confluence"; import type { ThinkingStep } from "@/components/tool-ui/deepagent-thinking"; -import { DisplayImageToolUI } from "@/components/tool-ui/display-image"; -import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast"; -import { GenerateReportToolUI } from "@/components/tool-ui/generate-report"; -import { GenerateVideoPresentationToolUI } from "@/components/tool-ui/video-presentation"; -import { - CreateGmailDraftToolUI, - SendGmailEmailToolUI, - TrashGmailEmailToolUI, - UpdateGmailDraftToolUI, -} from "@/components/tool-ui/gmail"; -import { - CreateCalendarEventToolUI, - DeleteCalendarEventToolUI, - UpdateCalendarEventToolUI, -} from "@/components/tool-ui/google-calendar"; -import { - CreateGoogleDriveFileToolUI, - DeleteGoogleDriveFileToolUI, -} from "@/components/tool-ui/google-drive"; -import { - CreateJiraIssueToolUI, - DeleteJiraIssueToolUI, - UpdateJiraIssueToolUI, -} from "@/components/tool-ui/jira"; -import { - CreateLinearIssueToolUI, - DeleteLinearIssueToolUI, - UpdateLinearIssueToolUI, -} from "@/components/tool-ui/linear"; -import { LinkPreviewToolUI } from "@/components/tool-ui/link-preview"; -import { - CreateNotionPageToolUI, - DeleteNotionPageToolUI, - UpdateNotionPageToolUI, -} from "@/components/tool-ui/notion"; -import { SandboxExecuteToolUI } from "@/components/tool-ui/sandbox-execute"; -import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage"; -import { RecallMemoryToolUI, SaveMemoryToolUI } from "@/components/tool-ui/user-memory"; import { Skeleton } from "@/components/ui/skeleton"; import { useChatSessionStateSync } from "@/hooks/use-chat-session-state"; import { useMessagesElectric } from "@/hooks/use-messages-electric"; import { documentsApiService } from "@/lib/apis/documents-api.service"; -// import { WriteTodosToolUI } from "@/components/tool-ui/write-todos"; import { getBearerToken } from "@/lib/auth-utils"; import { convertToThreadMessage } from "@/lib/chat/message-utils"; import { @@ -1719,37 +1675,6 @@ export default function NewChatPage() { return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* Disabled for now */}
diff --git a/surfsense_web/components/assistant-ui/assistant-message.tsx b/surfsense_web/components/assistant-ui/assistant-message.tsx index 4af5b07ee..72fed42ae 100644 --- a/surfsense_web/components/assistant-ui/assistant-message.tsx +++ b/surfsense_web/components/assistant-ui/assistant-message.tsx @@ -1,10 +1,9 @@ import { ActionBarPrimitive, - AssistantIf, + AuiIf, ErrorPrimitive, MessagePrimitive, - useAssistantState, - useMessage, + useAuiState, } from "@assistant-ui/react"; import { useAtomValue } from "jotai"; import { CheckIcon, CopyIcon, DownloadIcon, MessageSquare, RefreshCwIcon } from "lucide-react"; @@ -21,6 +20,22 @@ import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { CommentPanelContainer } from "@/components/chat-comments/comment-panel-container/comment-panel-container"; import { CommentSheet } from "@/components/chat-comments/comment-sheet/comment-sheet"; +import { CreateConfluencePageToolUI, DeleteConfluencePageToolUI, UpdateConfluencePageToolUI } from "@/components/tool-ui/confluence"; +import { DeepAgentThinkingToolUI } from "@/components/tool-ui/deepagent-thinking"; +import { DisplayImageToolUI } from "@/components/tool-ui/display-image"; +import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast"; +import { GenerateReportToolUI } from "@/components/tool-ui/generate-report"; +import { GenerateVideoPresentationToolUI } from "@/components/tool-ui/video-presentation"; +import { CreateGmailDraftToolUI, SendGmailEmailToolUI, TrashGmailEmailToolUI, UpdateGmailDraftToolUI } from "@/components/tool-ui/gmail"; +import { CreateCalendarEventToolUI, DeleteCalendarEventToolUI, UpdateCalendarEventToolUI } from "@/components/tool-ui/google-calendar"; +import { CreateGoogleDriveFileToolUI, DeleteGoogleDriveFileToolUI } from "@/components/tool-ui/google-drive"; +import { CreateJiraIssueToolUI, DeleteJiraIssueToolUI, UpdateJiraIssueToolUI } from "@/components/tool-ui/jira"; +import { CreateLinearIssueToolUI, DeleteLinearIssueToolUI, UpdateLinearIssueToolUI } from "@/components/tool-ui/linear"; +import { LinkPreviewToolUI, MultiLinkPreviewToolUI } from "@/components/tool-ui/link-preview"; +import { CreateNotionPageToolUI, DeleteNotionPageToolUI, UpdateNotionPageToolUI } from "@/components/tool-ui/notion"; +import { SandboxExecuteToolUI } from "@/components/tool-ui/sandbox-execute"; +import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage"; +import { RecallMemoryToolUI, SaveMemoryToolUI } from "@/components/tool-ui/user-memory"; import { useComments } from "@/hooks/use-comments"; import { useMediaQuery } from "@/hooks/use-media-query"; import { cn } from "@/lib/utils"; @@ -42,13 +57,13 @@ const ThinkingStepsPart: FC = () => { const thinkingStepsMap = useContext(ThinkingStepsContext); // Get the current message ID to look up thinking steps - const messageId = useAssistantState(({ message }) => message?.id); + const messageId = useAuiState(({ message }) => message?.id); const thinkingSteps = thinkingStepsMap.get(messageId) || []; // Check if this specific message is currently streaming // A message is streaming if: thread is running AND this is the last assistant message - const isThreadRunning = useAssistantState(({ thread }) => thread.isRunning); - const isLastMessage = useAssistantState(({ message }) => message?.isLast ?? false); + const isThreadRunning = useAuiState(({ thread }) => thread.isRunning); + const isLastMessage = useAuiState(({ message }) => message?.isLast ?? false); const isMessageStreaming = isThreadRunning && isLastMessage; if (thinkingSteps.length === 0) return null; @@ -70,7 +85,43 @@ const AssistantMessageInner: FC = () => { @@ -95,7 +146,7 @@ export const AssistantMessage: FC = () => { const messageRef = useRef(null); const commentPanelRef = useRef(null); const commentTriggerRef = useRef(null); - const messageId = useAssistantState(({ message }) => message?.id); + const messageId = useAuiState(({ message }) => message?.id); const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const dbMessageId = parseMessageId(messageId); const commentsEnabled = useAtomValue(commentsEnabledAtom); @@ -104,8 +155,8 @@ export const AssistantMessage: FC = () => { const isMediumScreen = useMediaQuery("(min-width: 768px) and (max-width: 1023px)"); const isDesktop = useMediaQuery("(min-width: 1024px)"); - const isThreadRunning = useAssistantState(({ thread }) => thread.isRunning); - const isLastMessage = useAssistantState(({ message }) => message?.isLast ?? false); + const isThreadRunning = useAuiState(({ thread }) => thread.isRunning); + const isLastMessage = useAuiState(({ message }) => message?.isLast ?? false); const isMessageStreaming = isThreadRunning && isLastMessage; const { data: commentsData, isSuccess: commentsLoaded } = useComments({ @@ -227,38 +278,38 @@ export const AssistantMessage: FC = () => { }; const AssistantActionBar: FC = () => { - const { isLast } = useMessage(); + const isLast = useAuiState((s) => s.message.isLast); return ( - - + - message.isCopied}> + message.isCopied}> - - !message.isCopied}> + + !message.isCopied}> - + - + - {/* Only allow regenerating the last assistant message */} - {isLast && ( + {/* Only allow regenerating the last assistant message */} + {isLast && ( )} - - ); + + ); }; diff --git a/surfsense_web/components/assistant-ui/thinking-steps.tsx b/surfsense_web/components/assistant-ui/thinking-steps.tsx index c773824f8..af87b7c59 100644 --- a/surfsense_web/components/assistant-ui/thinking-steps.tsx +++ b/surfsense_web/components/assistant-ui/thinking-steps.tsx @@ -1,7 +1,6 @@ -import { useAssistantState, useThreadViewport } from "@assistant-ui/react"; import { ChevronRightIcon } from "lucide-react"; import type { FC } from "react"; -import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react"; +import { createContext, useCallback, useEffect, useState } from "react"; import { ChainOfThoughtItem } from "@/components/prompt-kit/chain-of-thought"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; import type { ThinkingStep } from "@/components/tool-ui/deepagent-thinking"; @@ -154,52 +153,3 @@ export const ThinkingStepsDisplay: FC<{ steps: ThinkingStep[]; isThreadRunning?: ); }; -/** - * Component that handles auto-scroll when thinking steps update. - * Uses useThreadViewport to scroll to bottom when thinking steps change, - * ensuring the user always sees the latest content during streaming. - */ -export const ThinkingStepsScrollHandler: FC = () => { - const thinkingStepsMap = useContext(ThinkingStepsContext); - const viewport = useThreadViewport(); - const isRunning = useAssistantState(({ thread }) => thread.isRunning); - // Track the serialized state to detect any changes - const prevStateRef = useRef(""); - - useEffect(() => { - // Only act during streaming - if (!isRunning) { - prevStateRef.current = ""; - return; - } - - // Serialize the thinking steps state to detect any changes - // This catches new steps, status changes, and item additions - let stateString = ""; - thinkingStepsMap.forEach((steps, msgId) => { - steps.forEach((step) => { - stateString += `${msgId}:${step.id}:${step.status}:${step.items?.length || 0};`; - }); - }); - - // If state changed at all during streaming, scroll - if (stateString !== prevStateRef.current && stateString !== "") { - prevStateRef.current = stateString; - - // Multiple attempts to ensure scroll happens after DOM updates - const scrollAttempt = () => { - try { - viewport.scrollToBottom(); - } catch { - // Ignore errors - viewport might not be ready - } - }; - - // Delayed attempts to handle async DOM updates - requestAnimationFrame(scrollAttempt); - setTimeout(scrollAttempt, 100); - } - }, [thinkingStepsMap, viewport, isRunning]); - - return null; // This component doesn't render anything -}; diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index fa5595de4..e8c765d8c 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -1,13 +1,13 @@ import { ActionBarPrimitive, - AssistantIf, + AuiIf, BranchPickerPrimitive, ComposerPrimitive, ErrorPrimitive, MessagePrimitive, ThreadPrimitive, - useAssistantState, - useComposerRuntime, + useAui, + useAuiState, } from "@assistant-ui/react"; import { useAtom, useAtomValue, useSetAtom } from "jotai"; import { @@ -125,19 +125,19 @@ export const Thread: FC = ({ messageThinkingSteps = new Map() }) => const ThreadContent: FC = () => { return ( - - - thread.isEmpty}> + thread.isEmpty}> - + { style={{ paddingBottom: "max(1rem, env(safe-area-inset-bottom))" }} > - !thread.isEmpty}> + !thread.isEmpty}>
-
+
-
- ); + + ); }; const ThreadScrollToBottom: FC = () => { @@ -327,11 +327,11 @@ const Composer: FC = () => { const editorContainerRef = useRef(null); const documentPickerRef = useRef(null); const { search_space_id, chat_id } = useParams(); - const composerRuntime = useComposerRuntime(); + const aui = useAui(); const hasAutoFocusedRef = useRef(false); - const isThreadEmpty = useAssistantState(({ thread }) => thread.isEmpty); - const isThreadRunning = useAssistantState(({ thread }) => thread.isRunning); + const isThreadEmpty = useAuiState(({ thread }) => thread.isEmpty); + const isThreadRunning = useAuiState(({ thread }) => thread.isRunning); // Cycling placeholder state - only cycles in new chats const [placeholderIndex, setPlaceholderIndex] = useState(0); @@ -378,7 +378,7 @@ const Composer: FC = () => { // hooks never fire their own network requests (eliminates N+1 API calls). // Return a primitive string from the selector so useSyncExternalStore can // compare snapshots by value and avoid infinite re-render loops. - const assistantIdsKey = useAssistantState(({ thread }) => + const assistantIdsKey = useAuiState(({ thread }) => thread.messages .filter((m) => m.role === "assistant" && m.id?.startsWith("msg-")) .map((m) => m.id?.replace("msg-", "")) @@ -414,9 +414,9 @@ const Composer: FC = () => { // Sync editor text with assistant-ui composer runtime const handleEditorChange = useCallback( (text: string) => { - composerRuntime.setText(text); + aui.composer().setText(text); }, - [composerRuntime] + [aui] ); // Open document picker when @ mention is triggered @@ -469,7 +469,7 @@ const Composer: FC = () => { return; } if (!showDocumentPopover) { - composerRuntime.send(); + aui.composer().send(); editorRef.current?.clear(); setMentionedDocuments([]); setSidebarDocs([]); @@ -478,7 +478,7 @@ const Composer: FC = () => { showDocumentPopover, isThreadRunning, isBlockedByOtherUser, - composerRuntime, + aui, setMentionedDocuments, setSidebarDocs, ]); @@ -591,7 +591,7 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= 2; setToolsScrollPos(atTop ? "top" : atBottom ? "bottom" : "middle"); }, []); - const isComposerTextEmpty = useAssistantState(({ composer }) => { + const isComposerTextEmpty = useAuiState(({ composer }) => { const text = composer.text?.trim() || ""; return text.length === 0; }); @@ -702,8 +702,8 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false const isSendDisabled = isComposerEmpty || !hasModelConfigured || isBlockedByOtherUser; return ( -
-
+
+
{!isDesktop ? ( <> @@ -1007,16 +1007,14 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false )}
- - {!hasModelConfigured && ( + {!hasModelConfigured && (
Select a model
)} - -
- !thread.isRunning}> +
+ !thread.isRunning}> = ({ isBlockedByOtherUser = false - + - thread.isRunning}> + thread.isRunning}> - +
-
- ); +
+ ); }; /** Convert snake_case tool names to human-readable labels */ @@ -1151,13 +1149,13 @@ const ThinkingStepsPart: FC = () => { const thinkingStepsMap = useContext(ThinkingStepsContext); // Get the current message ID to look up thinking steps - const messageId = useAssistantState(({ message }) => message?.id); + const messageId = useAuiState(({ message }) => message?.id); const thinkingSteps = thinkingStepsMap.get(messageId) || []; // Check if this specific message is currently streaming // A message is streaming if: thread is running AND this is the last assistant message - const isThreadRunning = useAssistantState(({ thread }) => thread.isRunning); - const isLastMessage = useAssistantState(({ message }) => message?.isLast ?? false); + const isThreadRunning = useAuiState(({ thread }) => thread.isRunning); + const isLastMessage = useAuiState(({ message }) => message?.isLast ?? false); const isMessageStreaming = isThreadRunning && isLastMessage; if (thinkingSteps.length === 0) return null; @@ -1195,34 +1193,34 @@ const AssistantMessageInner: FC = () => { const AssistantActionBar: FC = () => { return ( - - + - message.isCopied}> + message.isCopied}> - - !message.isCopied}> + + !message.isCopied}> - + - + - + - - ); + + ); }; const EditComposer: FC = () => { diff --git a/surfsense_web/components/assistant-ui/user-message.tsx b/surfsense_web/components/assistant-ui/user-message.tsx index 1c0525277..9613b9964 100644 --- a/surfsense_web/components/assistant-ui/user-message.tsx +++ b/surfsense_web/components/assistant-ui/user-message.tsx @@ -1,4 +1,4 @@ -import { ActionBarPrimitive, MessagePrimitive, useAssistantState } from "@assistant-ui/react"; +import { ActionBarPrimitive, MessagePrimitive, useAuiState } from "@assistant-ui/react"; import { useAtomValue } from "jotai"; import { FileText, Pen } from "lucide-react"; import { type FC, useState } from "react"; @@ -42,10 +42,10 @@ const UserAvatar: FC = ({ displayName, avatarUrl }) => { }; export const UserMessage: FC = () => { - const messageId = useAssistantState(({ message }) => message?.id); + const messageId = useAuiState(({ message }) => message?.id); const messageDocumentsMap = useAtomValue(messageDocumentsMapAtom); const mentionedDocs = messageId ? messageDocumentsMap[messageId] : undefined; - const metadata = useAssistantState(({ message }) => message?.metadata); + const metadata = useAuiState(({ message }) => message?.metadata); const author = metadata?.custom?.author as AuthorMetadata | undefined; return ( @@ -93,13 +93,13 @@ export const UserMessage: FC = () => { }; const UserActionBar: FC = () => { - const isThreadRunning = useAssistantState(({ thread }) => thread.isRunning); + const isThreadRunning = useAuiState(({ thread }) => thread.isRunning); // Get current message ID - const currentMessageId = useAssistantState(({ message }) => message?.id); + const currentMessageId = useAuiState(({ message }) => message?.id); // Find the last user message ID in the thread (computed once, memoized by selector) - const lastUserMessageId = useAssistantState(({ thread }) => { + const lastUserMessageId = useAuiState(({ thread }) => { const messages = thread.messages; for (let i = messages.length - 1; i >= 0; i--) { if (messages[i].role === "user") { diff --git a/surfsense_web/components/public-chat/public-chat-view.tsx b/surfsense_web/components/public-chat/public-chat-view.tsx index cafc0d1a3..52830f601 100644 --- a/surfsense_web/components/public-chat/public-chat-view.tsx +++ b/surfsense_web/components/public-chat/public-chat-view.tsx @@ -3,12 +3,6 @@ import { AssistantRuntimeProvider } from "@assistant-ui/react"; import { Navbar } from "@/components/homepage/navbar"; import { ReportPanel } from "@/components/report-panel/report-panel"; -import { DisplayImageToolUI } from "@/components/tool-ui/display-image"; -import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast"; -import { GenerateReportToolUI } from "@/components/tool-ui/generate-report"; -import { GenerateVideoPresentationToolUI } from "@/components/tool-ui/video-presentation"; -import { LinkPreviewToolUI } from "@/components/tool-ui/link-preview"; -import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage"; import { Spinner } from "@/components/ui/spinner"; import { usePublicChat } from "@/hooks/use-public-chat"; import { usePublicChatRuntime } from "@/hooks/use-public-chat-runtime"; @@ -45,14 +39,6 @@ export function PublicChatView({ shareToken }: PublicChatViewProps) {
- {/* Tool UIs for rendering tool results */} - - - - - - -
} /> diff --git a/surfsense_web/components/public-chat/public-thread.tsx b/surfsense_web/components/public-chat/public-thread.tsx index 9b31a1a02..3e6cf663c 100644 --- a/surfsense_web/components/public-chat/public-thread.tsx +++ b/surfsense_web/components/public-chat/public-thread.tsx @@ -2,16 +2,22 @@ import { ActionBarPrimitive, - AssistantIf, + AuiIf, MessagePrimitive, ThreadPrimitive, - useAssistantState, + useAuiState, } from "@assistant-ui/react"; import { CheckIcon, CopyIcon } from "lucide-react"; import { type FC, type ReactNode, useState } from "react"; import { MarkdownText } from "@/components/assistant-ui/markdown-text"; import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; +import { DisplayImageToolUI } from "@/components/tool-ui/display-image"; +import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast"; +import { GenerateReportToolUI } from "@/components/tool-ui/generate-report"; +import { GenerateVideoPresentationToolUI } from "@/components/tool-ui/video-presentation"; +import { LinkPreviewToolUI } from "@/components/tool-ui/link-preview"; +import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage"; interface PublicThreadProps { footer?: ReactNode; @@ -93,7 +99,7 @@ const UserAvatar: FC void } }; const PublicUserMessage: FC = () => { - const metadata = useAssistantState(({ message }) => message?.metadata); + const metadata = useAuiState(({ message }) => message?.metadata); const author = metadata?.custom?.author as AuthorMetadata | undefined; return ( @@ -139,7 +145,17 @@ const PublicAssistantMessage: FC = () => {
@@ -153,21 +169,21 @@ const PublicAssistantMessage: FC = () => { const PublicAssistantActionBar: FC = () => { return ( - - + - message.isCopied}> + message.isCopied}> - - !message.isCopied}> + + !message.isCopied}> - + - - ); + + ); }; diff --git a/surfsense_web/components/tool-ui/confluence/create-confluence-page.tsx b/surfsense_web/components/tool-ui/confluence/create-confluence-page.tsx index ea4434852..f0f44e0c7 100644 --- a/surfsense_web/components/tool-ui/confluence/create-confluence-page.tsx +++ b/surfsense_web/components/tool-ui/confluence/create-confluence-page.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -457,12 +457,10 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const CreateConfluencePageToolUI = makeAssistantToolUI< +export const CreateConfluencePageToolUI = ({ args, result }: ToolCallMessagePartProps< { title: string; content?: string; space_id?: string }, CreateConfluencePageResult ->({ - toolName: "create_confluence_page", - render: function CreateConfluencePageUI({ args, result }) { +>) => { if (!result) return null; if (isInterruptResult(result)) { @@ -494,5 +492,4 @@ export const CreateConfluencePageToolUI = makeAssistantToolUI< if (isErrorResult(result)) return ; return ; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/confluence/delete-confluence-page.tsx b/surfsense_web/components/tool-ui/confluence/delete-confluence-page.tsx index 211ee3388..a4aa7409e 100644 --- a/surfsense_web/components/tool-ui/confluence/delete-confluence-page.tsx +++ b/surfsense_web/components/tool-ui/confluence/delete-confluence-page.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { CornerDownLeftIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; @@ -396,12 +396,10 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const DeleteConfluencePageToolUI = makeAssistantToolUI< +export const DeleteConfluencePageToolUI = ({ result }: ToolCallMessagePartProps< { page_title_or_id: string; delete_from_kb?: boolean }, DeleteConfluencePageResult ->({ - toolName: "delete_confluence_page", - render: function DeleteConfluencePageUI({ result }) { +>) => { if (!result) return null; if (isInterruptResult(result)) { @@ -435,5 +433,4 @@ export const DeleteConfluencePageToolUI = makeAssistantToolUI< if (isErrorResult(result)) return ; return ; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx b/surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx index 286981d51..afd652900 100644 --- a/surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx +++ b/surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; @@ -493,16 +493,14 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const UpdateConfluencePageToolUI = makeAssistantToolUI< +export const UpdateConfluencePageToolUI = ({ args, result }: ToolCallMessagePartProps< { page_title_or_id: string; new_title?: string; new_content?: string; }, UpdateConfluencePageResult ->({ - toolName: "update_confluence_page", - render: function UpdateConfluencePageUI({ args, result }) { +>) => { if (!result) return null; if (isInterruptResult(result)) { @@ -535,5 +533,4 @@ export const UpdateConfluencePageToolUI = makeAssistantToolUI< if (isErrorResult(result)) return ; return ; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/deepagent-thinking.tsx b/surfsense_web/components/tool-ui/deepagent-thinking.tsx index 3e6f668a8..c495c1723 100644 --- a/surfsense_web/components/tool-ui/deepagent-thinking.tsx +++ b/surfsense_web/components/tool-ui/deepagent-thinking.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { Brain, CheckCircle2, Loader2, Search, Sparkles } from "lucide-react"; import type { FC, ReactNode } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -317,12 +317,7 @@ const SmartChainOfThought: FC = ({ steps }) => { * when the deepagent is processing a query. It shows thinking steps * in a collapsible, hierarchical format. */ -export const DeepAgentThinkingToolUI = makeAssistantToolUI< - DeepAgentThinkingArgs, - DeepAgentThinkingResult ->({ - toolName: "deepagent_thinking", - render: function DeepAgentThinkingUI({ result, status }) { +export const DeepAgentThinkingToolUI = ({ result, status }: ToolCallMessagePartProps) => { // Loading state - tool is still running if (status.type === "running" || status.type === "requires-action") { return ; @@ -349,8 +344,7 @@ export const DeepAgentThinkingToolUI = makeAssistantToolUI<
); - }, -}); +}; // ============================================================================ // Public Components diff --git a/surfsense_web/components/tool-ui/display-image.tsx b/surfsense_web/components/tool-ui/display-image.tsx index b5fccbc78..824ce0628 100644 --- a/surfsense_web/components/tool-ui/display-image.tsx +++ b/surfsense_web/components/tool-ui/display-image.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { AlertCircleIcon, ImageIcon } from "lucide-react"; import { z } from "zod"; import { @@ -103,9 +103,7 @@ function ParsedImage({ result }: { result: unknown }) { * - Hover overlay effects * - Click to open full size */ -export const DisplayImageToolUI = makeAssistantToolUI({ - toolName: "display_image", - render: function DisplayImageUI({ args, result, status }) { +export const DisplayImageToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const src = args.src || "Unknown"; // Loading state - tool is still running @@ -154,8 +152,7 @@ export const DisplayImageToolUI = makeAssistantToolUI
); - }, -}); +}; export { DisplayImageArgsSchema, diff --git a/surfsense_web/components/tool-ui/generate-podcast.tsx b/surfsense_web/components/tool-ui/generate-podcast.tsx index bac5b3d5c..b4a4fe5e8 100644 --- a/surfsense_web/components/tool-ui/generate-podcast.tsx +++ b/surfsense_web/components/tool-ui/generate-podcast.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useParams, usePathname } from "next/navigation"; import { useCallback, useEffect, useRef, useState } from "react"; import { z } from "zod"; @@ -372,12 +372,7 @@ function PodcastStatusPoller({ podcastId, title }: { podcastId: number; title: s * * It polls for task completion and auto-updates when the podcast is ready. */ -export const GeneratePodcastToolUI = makeAssistantToolUI< - GeneratePodcastArgs, - GeneratePodcastResult ->({ - toolName: "generate_podcast", - render: function GeneratePodcastUI({ args, result, status }) { +export const GeneratePodcastToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const title = args.podcast_title || "SurfSense Podcast"; // Loading state - tool is still running (agent processing) @@ -462,5 +457,4 @@ export const GeneratePodcastToolUI = makeAssistantToolUI< // Fallback - missing required data return ; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/generate-report.tsx b/surfsense_web/components/tool-ui/generate-report.tsx index dd81d3403..33f283bbc 100644 --- a/surfsense_web/components/tool-ui/generate-report.tsx +++ b/surfsense_web/components/tool-ui/generate-report.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useAtomValue, useSetAtom } from "jotai"; import { Dot } from "lucide-react"; import { useParams, usePathname } from "next/navigation"; @@ -273,9 +273,7 @@ function ReportCard({ * Generate Report Tool UI — renders custom UI inline in chat * when the generate_report tool is called by the agent. */ -export const GenerateReportToolUI = makeAssistantToolUI({ - toolName: "generate_report", - render: function GenerateReportUI({ args, result, status }) { +export const GenerateReportToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const params = useParams(); const pathname = usePathname(); const isPublicRoute = pathname?.startsWith("/public/"); @@ -332,5 +330,4 @@ export const GenerateReportToolUI = makeAssistantToolUI; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/gmail/create-draft.tsx b/surfsense_web/components/tool-ui/gmail/create-draft.tsx index bca1bba80..aa8f58c72 100644 --- a/surfsense_web/components/tool-ui/gmail/create-draft.tsx +++ b/surfsense_web/components/tool-ui/gmail/create-draft.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen, UserIcon, UsersIcon } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -466,42 +466,42 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const CreateGmailDraftToolUI = makeAssistantToolUI< +export const CreateGmailDraftToolUI = ({ + args, + result, +}: ToolCallMessagePartProps< { to: string; subject: string; body: string; cc?: string; bcc?: string }, CreateGmailDraftResult ->({ - toolName: "create_gmail_draft", - render: function CreateGmailDraftUI({ args, result }) { - if (!result) return null; +>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + window.dispatchEvent( + new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) + ); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; - if (isErrorResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isInsufficientPermissionsResult(result)) + return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/gmail/send-email.tsx b/surfsense_web/components/tool-ui/gmail/send-email.tsx index d3cf9d639..fda375e51 100644 --- a/surfsense_web/components/tool-ui/gmail/send-email.tsx +++ b/surfsense_web/components/tool-ui/gmail/send-email.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, MailIcon, Pen, UserIcon, UsersIcon } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -464,42 +464,42 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const SendGmailEmailToolUI = makeAssistantToolUI< +export const SendGmailEmailToolUI = ({ + args, + result, +}: ToolCallMessagePartProps< { to: string; subject: string; body: string; cc?: string; bcc?: string }, SendGmailEmailResult ->({ - toolName: "send_gmail_email", - render: function SendGmailEmailUI({ args, result }) { - if (!result) return null; +>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + window.dispatchEvent( + new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) + ); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; - if (isErrorResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isInsufficientPermissionsResult(result)) + return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/gmail/trash-email.tsx b/surfsense_web/components/tool-ui/gmail/trash-email.tsx index d68c4b03f..f79d093f0 100644 --- a/surfsense_web/components/tool-ui/gmail/trash-email.tsx +++ b/surfsense_web/components/tool-ui/gmail/trash-email.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { CalendarIcon, CornerDownLeftIcon, MailIcon, UserIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; @@ -379,43 +379,42 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const TrashGmailEmailToolUI = makeAssistantToolUI< +export const TrashGmailEmailToolUI = ({ + result, +}: ToolCallMessagePartProps< { email_subject_or_id: string; delete_from_kb?: boolean }, TrashGmailEmailResult ->({ - toolName: "trash_gmail_email", - render: function TrashGmailEmailUI({ result }) { - if (!result) return null; +>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + const event = new CustomEvent("hitl-decision", { + detail: { decisions: [decision] }, + }); + window.dispatchEvent(event); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; - if (isNotFoundResult(result)) return ; - if (isErrorResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isInsufficientPermissionsResult(result)) + return ; + if (isNotFoundResult(result)) return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/gmail/update-draft.tsx b/surfsense_web/components/tool-ui/gmail/update-draft.tsx index 8c9fac109..7789368b2 100644 --- a/surfsense_web/components/tool-ui/gmail/update-draft.tsx +++ b/surfsense_web/components/tool-ui/gmail/update-draft.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, MailIcon, Pen, UserIcon, UsersIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; @@ -508,7 +508,10 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const UpdateGmailDraftToolUI = makeAssistantToolUI< +export const UpdateGmailDraftToolUI = ({ + args, + result, +}: ToolCallMessagePartProps< { draft_subject_or_id: string; body: string; @@ -518,42 +521,39 @@ export const UpdateGmailDraftToolUI = makeAssistantToolUI< bcc?: string; }, UpdateGmailDraftResult ->({ - toolName: "update_gmail_draft", - render: function UpdateGmailDraftUI({ args, result }) { - if (!result) return null; +>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - window.dispatchEvent( - new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }) - ); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + window.dispatchEvent( + new CustomEvent("hitl-decision", { + detail: { decisions: [decision] }, + }) + ); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; - if (isNotFoundResult(result)) return ; - if (isErrorResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isInsufficientPermissionsResult(result)) + return ; + if (isNotFoundResult(result)) return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/google-calendar/create-event.tsx b/surfsense_web/components/tool-ui/google-calendar/create-event.tsx index 8d337c8f7..a2e23dd36 100644 --- a/surfsense_web/components/tool-ui/google-calendar/create-event.tsx +++ b/surfsense_web/components/tool-ui/google-calendar/create-event.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { ClockIcon, CornerDownLeftIcon, GlobeIcon, MapPinIcon, Pen, UsersIcon } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -606,7 +606,10 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const CreateCalendarEventToolUI = makeAssistantToolUI< +export const CreateCalendarEventToolUI = ({ + args, + result, +}: ToolCallMessagePartProps< { summary: string; start_datetime: string; @@ -616,39 +619,36 @@ export const CreateCalendarEventToolUI = makeAssistantToolUI< attendees?: string[]; }, CreateCalendarEventResult ->({ - toolName: "create_calendar_event", - render: function CreateCalendarEventUI({ args, result }) { - if (!result) return null; +>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + window.dispatchEvent( + new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) + ); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; - if (isErrorResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isInsufficientPermissionsResult(result)) + return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/google-calendar/delete-event.tsx b/surfsense_web/components/tool-ui/google-calendar/delete-event.tsx index 8df47c3d8..404a6ced2 100644 --- a/surfsense_web/components/tool-ui/google-calendar/delete-event.tsx +++ b/surfsense_web/components/tool-ui/google-calendar/delete-event.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { CalendarIcon, ClockIcon, CornerDownLeftIcon, MapPinIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; @@ -431,44 +431,43 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const DeleteCalendarEventToolUI = makeAssistantToolUI< +export const DeleteCalendarEventToolUI = ({ + result, +}: ToolCallMessagePartProps< { event_title_or_id: string; delete_from_kb?: boolean }, DeleteCalendarEventResult ->({ - toolName: "delete_calendar_event", - render: function DeleteCalendarEventUI({ result }) { - if (!result) return null; +>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + const event = new CustomEvent("hitl-decision", { + detail: { decisions: [decision] }, + }); + window.dispatchEvent(event); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isNotFoundResult(result)) return ; - if (isWarningResult(result)) return ; - if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; - if (isErrorResult(result)) return ; + if (isNotFoundResult(result)) return ; + if (isWarningResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isInsufficientPermissionsResult(result)) + return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/google-calendar/update-event.tsx b/surfsense_web/components/tool-ui/google-calendar/update-event.tsx index c5e261c11..cc941bab8 100644 --- a/surfsense_web/components/tool-ui/google-calendar/update-event.tsx +++ b/surfsense_web/components/tool-ui/google-calendar/update-event.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { ArrowRightIcon, @@ -653,7 +653,10 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const UpdateCalendarEventToolUI = makeAssistantToolUI< +export const UpdateCalendarEventToolUI = ({ + args, + result, +}: ToolCallMessagePartProps< { event_ref: string; new_summary?: string; @@ -664,40 +667,37 @@ export const UpdateCalendarEventToolUI = makeAssistantToolUI< new_attendees?: string[]; }, UpdateCalendarEventResult ->({ - toolName: "update_calendar_event", - render: function UpdateCalendarEventUI({ args, result }) { - if (!result) return null; +>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + window.dispatchEvent( + new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) + ); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isNotFoundResult(result)) return ; - if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; - if (isErrorResult(result)) return ; + if (isNotFoundResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isInsufficientPermissionsResult(result)) + return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/google-drive/create-file.tsx b/surfsense_web/components/tool-ui/google-drive/create-file.tsx index c0b38db8e..3b0da4a4d 100644 --- a/surfsense_web/components/tool-ui/google-drive/create-file.tsx +++ b/surfsense_web/components/tool-ui/google-drive/create-file.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, FileIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -492,44 +492,38 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const CreateGoogleDriveFileToolUI = makeAssistantToolUI< - { name: string; file_type: string; content?: string }, - CreateGoogleDriveFileResult ->({ - toolName: "create_google_drive_file", - render: function CreateGoogleDriveFileUI({ args, result }) { - if (!result) return null; +export const CreateGoogleDriveFileToolUI = ({ args, result }: ToolCallMessagePartProps<{ name: string; file_type: string; content?: string }, CreateGoogleDriveFileResult>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + window.dispatchEvent( + new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) + ); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isAuthErrorResult(result)) return ; + if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; + if (isInsufficientPermissionsResult(result)) + return ; - if (isErrorResult(result)) return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/google-drive/trash-file.tsx b/surfsense_web/components/tool-ui/google-drive/trash-file.tsx index 35559bb30..71d20e736 100644 --- a/surfsense_web/components/tool-ui/google-drive/trash-file.tsx +++ b/surfsense_web/components/tool-ui/google-drive/trash-file.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { CornerDownLeftIcon, InfoIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; @@ -410,46 +410,40 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const DeleteGoogleDriveFileToolUI = makeAssistantToolUI< - { file_name: string; delete_from_kb?: boolean }, - DeleteGoogleDriveFileResult ->({ - toolName: "delete_google_drive_file", - render: function DeleteGoogleDriveFileUI({ result }) { - if (!result) return null; +export const DeleteGoogleDriveFileToolUI = ({ result }: ToolCallMessagePartProps<{ file_name: string; delete_from_kb?: boolean }, DeleteGoogleDriveFileResult>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + const event = new CustomEvent("hitl-decision", { + detail: { decisions: [decision] }, + }); + window.dispatchEvent(event); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isAuthErrorResult(result)) return ; + if (isAuthErrorResult(result)) return ; - if (isInsufficientPermissionsResult(result)) - return ; + if (isInsufficientPermissionsResult(result)) + return ; - if (isNotFoundResult(result)) return ; - if (isWarningResult(result)) return ; - if (isErrorResult(result)) return ; + if (isNotFoundResult(result)) return ; + if (isWarningResult(result)) return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/jira/create-jira-issue.tsx b/surfsense_web/components/tool-ui/jira/create-jira-issue.tsx index 67dac58b1..92a9c521c 100644 --- a/surfsense_web/components/tool-ui/jira/create-jira-issue.tsx +++ b/surfsense_web/components/tool-ui/jira/create-jira-issue.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -536,7 +536,7 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const CreateJiraIssueToolUI = makeAssistantToolUI< +export const CreateJiraIssueToolUI = ({ args, result }: ToolCallMessagePartProps< { project_key: string; summary: string; @@ -545,9 +545,7 @@ export const CreateJiraIssueToolUI = makeAssistantToolUI< priority?: string; }, CreateJiraIssueResult ->({ - toolName: "create_jira_issue", - render: function CreateJiraIssueUI({ args, result }) { +>) => { if (!result) return null; if (isInterruptResult(result)) { @@ -579,5 +577,4 @@ export const CreateJiraIssueToolUI = makeAssistantToolUI< if (isErrorResult(result)) return ; return ; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/jira/delete-jira-issue.tsx b/surfsense_web/components/tool-ui/jira/delete-jira-issue.tsx index 0ad5be5bd..26a7ec20b 100644 --- a/surfsense_web/components/tool-ui/jira/delete-jira-issue.tsx +++ b/surfsense_web/components/tool-ui/jira/delete-jira-issue.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { CornerDownLeftIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; @@ -393,12 +393,10 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const DeleteJiraIssueToolUI = makeAssistantToolUI< +export const DeleteJiraIssueToolUI = ({ result }: ToolCallMessagePartProps< { issue_title_or_key: string; delete_from_kb?: boolean }, DeleteJiraIssueResult ->({ - toolName: "delete_jira_issue", - render: function DeleteJiraIssueUI({ result }) { +>) => { if (!result) return null; if (isInterruptResult(result)) { @@ -432,5 +430,4 @@ export const DeleteJiraIssueToolUI = makeAssistantToolUI< if (isErrorResult(result)) return ; return ; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/jira/update-jira-issue.tsx b/surfsense_web/components/tool-ui/jira/update-jira-issue.tsx index f085a04d6..7d1547b48 100644 --- a/surfsense_web/components/tool-ui/jira/update-jira-issue.tsx +++ b/surfsense_web/components/tool-ui/jira/update-jira-issue.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; @@ -553,7 +553,7 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const UpdateJiraIssueToolUI = makeAssistantToolUI< +export const UpdateJiraIssueToolUI = ({ args, result }: ToolCallMessagePartProps< { issue_title_or_key: string; new_summary?: string; @@ -561,9 +561,7 @@ export const UpdateJiraIssueToolUI = makeAssistantToolUI< new_priority?: string; }, UpdateJiraIssueResult ->({ - toolName: "update_jira_issue", - render: function UpdateJiraIssueUI({ args, result }) { +>) => { if (!result) return null; if (isInterruptResult(result)) { @@ -596,5 +594,4 @@ export const UpdateJiraIssueToolUI = makeAssistantToolUI< if (isErrorResult(result)) return ; return ; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/linear/create-linear-issue.tsx b/surfsense_web/components/tool-ui/linear/create-linear-issue.tsx index 39e689a46..394af8306 100644 --- a/surfsense_web/components/tool-ui/linear/create-linear-issue.tsx +++ b/surfsense_web/components/tool-ui/linear/create-linear-issue.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -605,40 +605,34 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const CreateLinearIssueToolUI = makeAssistantToolUI< - { title: string; description?: string }, - CreateLinearIssueResult ->({ - toolName: "create_linear_issue", - render: function CreateLinearIssueUI({ args, result }) { - if (!result) return null; +export const CreateLinearIssueToolUI = ({ args, result }: ToolCallMessagePartProps<{ title: string; description?: string }, CreateLinearIssueResult>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + window.dispatchEvent( + new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) + ); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isAuthErrorResult(result)) return ; - if (isErrorResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/linear/delete-linear-issue.tsx b/surfsense_web/components/tool-ui/linear/delete-linear-issue.tsx index 592f01555..5302c7646 100644 --- a/surfsense_web/components/tool-ui/linear/delete-linear-issue.tsx +++ b/surfsense_web/components/tool-ui/linear/delete-linear-issue.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { CornerDownLeftIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; @@ -360,42 +360,36 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const DeleteLinearIssueToolUI = makeAssistantToolUI< - { issue_ref: string; delete_from_kb?: boolean }, - DeleteLinearIssueResult ->({ - toolName: "delete_linear_issue", - render: function DeleteLinearIssueUI({ result }) { - if (!result) return null; +export const DeleteLinearIssueToolUI = ({ result }: ToolCallMessagePartProps<{ issue_ref: string; delete_from_kb?: boolean }, DeleteLinearIssueResult>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + const event = new CustomEvent("hitl-decision", { + detail: { decisions: [decision] }, + }); + window.dispatchEvent(event); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isNotFoundResult(result)) return ; - if (isAuthErrorResult(result)) return ; - if (isWarningResult(result)) return ; - if (isErrorResult(result)) return ; + if (isNotFoundResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isWarningResult(result)) return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/linear/update-linear-issue.tsx b/surfsense_web/components/tool-ui/linear/update-linear-issue.tsx index 0b0aa4623..d43e89b7f 100644 --- a/surfsense_web/components/tool-ui/linear/update-linear-issue.tsx +++ b/surfsense_web/components/tool-ui/linear/update-linear-issue.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; @@ -739,49 +739,43 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const UpdateLinearIssueToolUI = makeAssistantToolUI< - { - issue_ref: string; - new_title?: string; - new_description?: string; - new_state_name?: string; - new_assignee_email?: string; - new_priority?: number; - new_label_names?: string[]; - }, - UpdateLinearIssueResult ->({ - toolName: "update_linear_issue", - render: function UpdateLinearIssueUI({ args, result }) { - if (!result) return null; +export const UpdateLinearIssueToolUI = ({ args, result }: ToolCallMessagePartProps<{ + issue_ref: string; + new_title?: string; + new_description?: string; + new_state_name?: string; + new_assignee_email?: string; + new_priority?: number; + new_label_names?: string[]; +}, UpdateLinearIssueResult>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + window.dispatchEvent( + new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) + ); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isNotFoundResult(result)) return ; - if (isAuthErrorResult(result)) return ; - if (isErrorResult(result)) return ; + if (isNotFoundResult(result)) return ; + if (isAuthErrorResult(result)) return ; + if (isErrorResult(result)) return ; - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/link-preview.tsx b/surfsense_web/components/tool-ui/link-preview.tsx index 5c1a952b2..7af00c5ba 100644 --- a/surfsense_web/components/tool-ui/link-preview.tsx +++ b/surfsense_web/components/tool-ui/link-preview.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { AlertCircleIcon, ExternalLinkIcon, LinkIcon } from "lucide-react"; import { z } from "zod"; import { @@ -111,9 +111,7 @@ function ParsedMediaCard({ result }: { result: unknown }) { * - Domain name * - Clickable link to open in new tab */ -export const LinkPreviewToolUI = makeAssistantToolUI({ - toolName: "link_preview", - render: function LinkPreviewUI({ args, result, status }) { +export const LinkPreviewToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const url = args.url || "Unknown URL"; // Loading state - tool is still running @@ -162,8 +160,7 @@ export const LinkPreviewToolUI = makeAssistantToolUI
); - }, -}); +}; // ============================================================================ // Multi Link Preview Schemas @@ -195,12 +192,7 @@ const MultiLinkPreviewResultSchema = z.object({ type MultiLinkPreviewArgs = z.infer; type MultiLinkPreviewResult = z.infer; -export const MultiLinkPreviewToolUI = makeAssistantToolUI< - MultiLinkPreviewArgs, - MultiLinkPreviewResult ->({ - toolName: "multi_link_preview", - render: function MultiLinkPreviewUI({ args, result, status }) { +export const MultiLinkPreviewToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const urls = args.urls || []; // Loading state @@ -244,8 +236,7 @@ export const MultiLinkPreviewToolUI = makeAssistantToolUI< ))}
); - }, -}); +}; export { LinkPreviewArgsSchema, diff --git a/surfsense_web/components/tool-ui/notion/create-notion-page.tsx b/surfsense_web/components/tool-ui/notion/create-notion-page.tsx index 3b89b3c4a..c3beb77f9 100644 --- a/surfsense_web/components/tool-ui/notion/create-notion-page.tsx +++ b/surfsense_web/components/tool-ui/notion/create-notion-page.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -445,46 +445,40 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const CreateNotionPageToolUI = makeAssistantToolUI< - { title: string; content: string }, - CreateNotionPageResult ->({ - toolName: "create_notion_page", - render: function CreateNotionPageUI({ args, result }) { - if (!result) return null; +export const CreateNotionPageToolUI = ({ args, result }: ToolCallMessagePartProps<{ title: string; content: string }, CreateNotionPageResult>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + const event = new CustomEvent("hitl-decision", { + detail: { decisions: [decision] }, + }); + window.dispatchEvent(event); + }} + /> + ); + } - if (isAuthErrorResult(result)) { - return ; - } + if (isAuthErrorResult(result)) { + return ; + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isErrorResult(result)) { - return ; - } + if (isErrorResult(result)) { + return ; + } - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/notion/delete-notion-page.tsx b/surfsense_web/components/tool-ui/notion/delete-notion-page.tsx index c3f78209d..4709bde01 100644 --- a/surfsense_web/components/tool-ui/notion/delete-notion-page.tsx +++ b/surfsense_web/components/tool-ui/notion/delete-notion-page.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { CornerDownLeftIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; @@ -372,53 +372,47 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const DeleteNotionPageToolUI = makeAssistantToolUI< - { page_title: string; delete_from_kb?: boolean }, - DeleteNotionPageResult ->({ - toolName: "delete_notion_page", - render: function DeleteNotionPageUI({ result }) { - if (!result) return null; +export const DeleteNotionPageToolUI = ({ result }: ToolCallMessagePartProps<{ page_title: string; delete_from_kb?: boolean }, DeleteNotionPageResult>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + const event = new CustomEvent("hitl-decision", { + detail: { decisions: [decision] }, + }); + window.dispatchEvent(event); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isInfoResult(result)) { - return ; - } + if (isInfoResult(result)) { + return ; + } - if (isWarningResult(result)) { - return ; - } + if (isWarningResult(result)) { + return ; + } - if (isAuthErrorResult(result)) { - return ; - } + if (isAuthErrorResult(result)) { + return ; + } - if (isErrorResult(result)) { - return ; - } + if (isErrorResult(result)) { + return ; + } - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/notion/update-notion-page.tsx b/surfsense_web/components/tool-ui/notion/update-notion-page.tsx index b3bb05117..244f57211 100644 --- a/surfsense_web/components/tool-ui/notion/update-notion-page.tsx +++ b/surfsense_web/components/tool-ui/notion/update-notion-page.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { CornerDownLeftIcon, Pen } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; @@ -395,50 +395,44 @@ function SuccessCard({ result }: { result: SuccessResult }) { ); } -export const UpdateNotionPageToolUI = makeAssistantToolUI< - { page_title: string; content: string }, - UpdateNotionPageResult ->({ - toolName: "update_notion_page", - render: function UpdateNotionPageUI({ args, result }) { - if (!result) return null; +export const UpdateNotionPageToolUI = ({ args, result }: ToolCallMessagePartProps<{ page_title: string; content: string }, UpdateNotionPageResult>) => { + if (!result) return null; - if (isInterruptResult(result)) { - return ( - { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} - /> - ); - } + if (isInterruptResult(result)) { + return ( + { + const event = new CustomEvent("hitl-decision", { + detail: { decisions: [decision] }, + }); + window.dispatchEvent(event); + }} + /> + ); + } - if ( - typeof result === "object" && - result !== null && - "status" in result && - (result as { status: string }).status === "rejected" - ) { - return null; - } + if ( + typeof result === "object" && + result !== null && + "status" in result && + (result as { status: string }).status === "rejected" + ) { + return null; + } - if (isInfoResult(result)) { - return ; - } + if (isInfoResult(result)) { + return ; + } - if (isAuthErrorResult(result)) { - return ; - } + if (isAuthErrorResult(result)) { + return ; + } - if (isErrorResult(result)) { - return ; - } + if (isErrorResult(result)) { + return ; + } - return ; - }, -}); + return ; +}; diff --git a/surfsense_web/components/tool-ui/sandbox-execute.tsx b/surfsense_web/components/tool-ui/sandbox-execute.tsx index d4e60de9e..2de1b7f43 100644 --- a/surfsense_web/components/tool-ui/sandbox-execute.tsx +++ b/surfsense_web/components/tool-ui/sandbox-execute.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { AlertCircleIcon, CheckCircle2Icon, @@ -380,9 +380,7 @@ function ExecuteCompleted({ // Tool UI // ============================================================================ -export const SandboxExecuteToolUI = makeAssistantToolUI({ - toolName: "execute", - render: function SandboxExecuteUI({ args, result, status }) { +export const SandboxExecuteToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const command = args.command || "…"; if (status.type === "running" || status.type === "requires-action") { @@ -414,7 +412,6 @@ export const SandboxExecuteToolUI = makeAssistantToolUI; - }, -}); +}; export { ExecuteArgsSchema, ExecuteResultSchema, type ExecuteArgs, type ExecuteResult }; diff --git a/surfsense_web/components/tool-ui/scrape-webpage.tsx b/surfsense_web/components/tool-ui/scrape-webpage.tsx index 87fae8868..a17c56734 100644 --- a/surfsense_web/components/tool-ui/scrape-webpage.tsx +++ b/surfsense_web/components/tool-ui/scrape-webpage.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { AlertCircleIcon, FileTextIcon } from "lucide-react"; import { z } from "zod"; import { @@ -104,9 +104,7 @@ function ParsedArticle({ result }: { result: unknown }) { * - Word count * - Link to original source */ -export const ScrapeWebpageToolUI = makeAssistantToolUI({ - toolName: "scrape_webpage", - render: function ScrapeWebpageUI({ args, result, status }) { +export const ScrapeWebpageToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const url = args.url || "Unknown URL"; // Loading state - tool is still running @@ -155,8 +153,7 @@ export const ScrapeWebpageToolUI = makeAssistantToolUI
); - }, -}); +}; export { ScrapeWebpageArgsSchema, diff --git a/surfsense_web/components/tool-ui/user-memory.tsx b/surfsense_web/components/tool-ui/user-memory.tsx index f7c002887..9fa4d2bc6 100644 --- a/surfsense_web/components/tool-ui/user-memory.tsx +++ b/surfsense_web/components/tool-ui/user-memory.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI } from "@assistant-ui/react"; +import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { BrainIcon, CheckIcon, Loader2Icon, SearchIcon, XIcon } from "lucide-react"; import { z } from "zod"; @@ -80,9 +80,7 @@ function CategoryBadge({ category }: { category: string }) { // Save Memory Tool UI // ============================================================================ -export const SaveMemoryToolUI = makeAssistantToolUI({ - toolName: "save_memory", - render: function SaveMemoryUI({ args, result, status }) { +export const SaveMemoryToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const isRunning = status.type === "running" || status.type === "requires-action"; const isComplete = status.type === "complete"; const isError = result?.status === "error"; @@ -159,16 +157,13 @@ export const SaveMemoryToolUI = makeAssistantToolUI({ - toolName: "recall_memory", - render: function RecallMemoryUI({ args, result, status }) { +export const RecallMemoryToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const isRunning = status.type === "running" || status.type === "requires-action"; const isComplete = status.type === "complete"; const isError = result?.status === "error"; @@ -263,8 +258,7 @@ export const RecallMemoryToolUI = makeAssistantToolUI; } -export const GenerateVideoPresentationToolUI = makeAssistantToolUI< +export const GenerateVideoPresentationToolUI = ({ args, result, status }: ToolCallMessagePartProps< GenerateVideoPresentationArgs, GenerateVideoPresentationResult ->({ - toolName: "generate_video_presentation", - render: function GenerateVideoPresentationUI({ args, result, status }) { +>) => { const params = useParams(); const pathname = usePathname(); const isPublicRoute = pathname?.startsWith("/public/"); @@ -705,5 +703,4 @@ export const GenerateVideoPresentationToolUI = makeAssistantToolUI< } return ; - }, -}); +}; diff --git a/surfsense_web/components/tool-ui/write-todos.tsx b/surfsense_web/components/tool-ui/write-todos.tsx index 9b959bd33..104cbcf44 100644 --- a/surfsense_web/components/tool-ui/write-todos.tsx +++ b/surfsense_web/components/tool-ui/write-todos.tsx @@ -1,6 +1,6 @@ "use client"; -import { makeAssistantToolUI, useAssistantState } from "@assistant-ui/react"; +import { type ToolCallMessagePartProps, useAuiState } from "@assistant-ui/react"; import { useAtomValue, useSetAtom } from "jotai"; import { useEffect, useMemo } from "react"; import { z } from "zod"; @@ -63,96 +63,98 @@ function WriteTodosLoading() { * only the FIRST component renders. Subsequent updates just update the * shared state, and the first component reads from it. */ -export const WriteTodosToolUI = makeAssistantToolUI({ - toolName: "write_todos", - render: function WriteTodosUI({ args, result, status, toolCallId }) { - const updatePlanState = useSetAtom(updatePlanStateAtom); - const planStates = useAtomValue(planStatesAtom); +export const WriteTodosToolUI = ({ + args, + result, + status, + toolCallId, +}: ToolCallMessagePartProps) => { + const updatePlanState = useSetAtom(updatePlanStateAtom); + const planStates = useAtomValue(planStatesAtom); - // Check if the THREAD is running - const isThreadRunning = useAssistantState(({ thread }) => thread.isRunning); + // Check if the THREAD is running + const isThreadRunning = useAuiState(({ thread }) => thread.isRunning); - // Use result if available, otherwise args (for streaming) - const data = result || args; - const hasTodos = data?.todos && data.todos.length > 0; + // Use result if available, otherwise args (for streaming) + const data = result || args; + const hasTodos = data?.todos && data.todos.length > 0; - // Fixed title for all plans in conversation - const planTitle = "Plan"; + // Fixed title for all plans in conversation + const planTitle = "Plan"; - // SYNCHRONOUS ownership check - const isOwner = useMemo(() => { - return registerPlanOwner(planTitle, toolCallId); - }, [planTitle, toolCallId]); + // SYNCHRONOUS ownership check + const isOwner = useMemo(() => { + return registerPlanOwner(planTitle, toolCallId); + }, [planTitle, toolCallId]); - // Get canonical title - const canonicalTitle = useMemo(() => getCanonicalPlanTitle(planTitle), [planTitle]); + // Get canonical title + const canonicalTitle = useMemo(() => getCanonicalPlanTitle(planTitle), [planTitle]); - // Register/update the plan state - useEffect(() => { - if (hasTodos) { - const normalizedPlan = parseSerializablePlan({ todos: data.todos }); - updatePlanState({ - id: normalizedPlan.id, - title: canonicalTitle, - todos: normalizedPlan.todos, - toolCallId, - }); - } - }, [data, hasTodos, canonicalTitle, updatePlanState, toolCallId]); - - // Get the current plan state - const currentPlanState = planStates.get(canonicalTitle); - - // If we're NOT the owner, render nothing - if (!isOwner) { - return null; + // Register/update the plan state + useEffect(() => { + if (hasTodos) { + const normalizedPlan = parseSerializablePlan({ todos: data.todos }); + updatePlanState({ + id: normalizedPlan.id, + title: canonicalTitle, + todos: normalizedPlan.todos, + toolCallId, + }); } + }, [data, hasTodos, canonicalTitle, updatePlanState, toolCallId]); - // Loading state - if (status.type === "running" || status.type === "requires-action") { - if (hasTodos) { - const plan = parseSerializablePlan({ todos: data.todos }); - return ( -
- - - -
- ); - } - return ; + // Get the current plan state + const currentPlanState = planStates.get(canonicalTitle); + + // If we're NOT the owner, render nothing + if (!isOwner) { + return null; + } + + // Loading state + if (status.type === "running" || status.type === "requires-action") { + if (hasTodos) { + const plan = parseSerializablePlan({ todos: data.todos }); + return ( +
+ + + +
+ ); } + return ; + } - // Incomplete/cancelled state - if (status.type === "incomplete") { - if (currentPlanState || hasTodos) { - const plan = currentPlanState || parseSerializablePlan({ todos: data?.todos || [] }); - return ( -
- - - -
- ); - } - return null; + // Incomplete/cancelled state + if (status.type === "incomplete") { + if (currentPlanState || hasTodos) { + const plan = currentPlanState || parseSerializablePlan({ todos: data?.todos || [] }); + return ( +
+ + + +
+ ); } + return null; + } - // Success - render the plan - const planToRender = - currentPlanState || (hasTodos ? parseSerializablePlan({ todos: data.todos }) : null); - if (!planToRender) { - return ; - } + // Success - render the plan + const planToRender = + currentPlanState || (hasTodos ? parseSerializablePlan({ todos: data.todos }) : null); + if (!planToRender) { + return ; + } - return ( -
- - - -
- ); - }, -}); + return ( +
+ + + +
+ ); +}; export { WriteTodosSchema, type WriteTodosData }; diff --git a/surfsense_web/package.json b/surfsense_web/package.json index 2a33c9ab2..67fe6260a 100644 --- a/surfsense_web/package.json +++ b/surfsense_web/package.json @@ -23,9 +23,8 @@ "dependencies": { "@ai-sdk/react": "^1.2.12", "@ariakit/react": "^0.4.21", - "@assistant-ui/react": "^0.11.53", - "@assistant-ui/react-ai-sdk": "^1.1.20", - "@assistant-ui/react-markdown": "^0.11.9", + "@assistant-ui/react": "^0.12.19", + "@assistant-ui/react-markdown": "^0.12.6", "@babel/standalone": "^7.29.2", "@electric-sql/client": "^1.4.0", "@electric-sql/pglite": "^0.3.14", diff --git a/surfsense_web/pnpm-lock.yaml b/surfsense_web/pnpm-lock.yaml index 51c3f3f1e..ee6da25a4 100644 --- a/surfsense_web/pnpm-lock.yaml +++ b/surfsense_web/pnpm-lock.yaml @@ -15,14 +15,11 @@ importers: specifier: ^0.4.21 version: 0.4.21(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@assistant-ui/react': - specifier: ^0.11.53 - version: 0.11.58(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) - '@assistant-ui/react-ai-sdk': - specifier: ^1.1.20 - version: 1.3.8(@assistant-ui/react@0.11.58(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)))(@types/react@19.2.14)(assistant-cloud@0.1.18)(react@19.2.4) + specifier: ^0.12.19 + version: 0.12.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) '@assistant-ui/react-markdown': - specifier: ^0.11.9 - version: 0.11.10(@assistant-ui/react@0.11.58(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)))(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + specifier: ^0.12.6 + version: 0.12.6(@assistant-ui/react@0.12.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)))(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@babel/standalone': specifier: ^7.29.2 version: 7.29.2 @@ -441,32 +438,16 @@ importers: packages: - '@ai-sdk/gateway@3.0.53': - resolution: {integrity: sha512-QT3FEoNARMRlk8JJVR7L98exiK9C8AGfrEJVbRxBT1yIXKs/N19o/+PsjTRVsARgDJNcy9JbJp1FspKucEat0Q==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@2.2.8': resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} engines: {node: '>=18'} peerDependencies: zod: ^3.23.8 - '@ai-sdk/provider-utils@4.0.15': - resolution: {integrity: sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider@1.1.3': resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} engines: {node: '>=18'} - '@ai-sdk/provider@3.0.8': - resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==} - engines: {node: '>=18'} - '@ai-sdk/react@1.2.12': resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} engines: {node: '>=18'} @@ -477,12 +458,6 @@ packages: zod: optional: true - '@ai-sdk/react@3.0.99': - resolution: {integrity: sha512-xMsp5br4Dpr/3BYq/jrE8q4YLgViU1KHVq8VB0+dzdLJFU3jKA83uoxpbWqzV/edQOBPgGBSb2CgmV5v77rvzA==} - engines: {node: '>=18'} - peerDependencies: - react: ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1 - '@ai-sdk/ui-utils@1.2.11': resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} engines: {node: '>=18'} @@ -508,31 +483,37 @@ packages: react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - '@assistant-ui/react-ai-sdk@1.3.8': - resolution: {integrity: sha512-LJ2k2r4SYDfH2gmd5xIsu7XBNGucN7ipLgzHmZ4nd8MX8/S/lBmfiNIUko7MPbwbauq6G4KPmRVsiJ5QrqIx6A==} + '@assistant-ui/core@0.1.7': + resolution: {integrity: sha512-219T42ihVOicbJXZLWgD2CW5Bylg9Nk7geC331X4RfJxTDYlm2zIjViGlGaqfj6URXBp6kMulO2BTUrHGmAvdw==} peerDependencies: - '@assistant-ui/react': ^0.12.11 + '@assistant-ui/store': ^0.2.3 + '@assistant-ui/tap': ^0.5.3 '@types/react': '*' - assistant-cloud: '*' + assistant-cloud: ^0.1.22 react: ^18 || ^19 + zustand: ^5.0.11 peerDependenciesMeta: '@types/react': optional: true assistant-cloud: optional: true + react: + optional: true + zustand: + optional: true - '@assistant-ui/react-markdown@0.11.10': - resolution: {integrity: sha512-7JFd9/s/ZzOtUAHfrxvij4Ti+4V42FVyjF9veWRUsGKKcw7bBZvBxyb2cBMr93sUf0R1eQHsIV39hZjil8J7lw==} + '@assistant-ui/react-markdown@0.12.6': + resolution: {integrity: sha512-utJqsdDXB3UVZfOa3ErLpaTHraeXkDshR0D34shWdTHrmLyx9e/HypTu4+BgiSsxS+ME6t9WO9M3VeGDprfUcQ==} peerDependencies: - '@assistant-ui/react': ^0.11.58 + '@assistant-ui/react': ^0.12.19 '@types/react': '*' react: ^18 || ^19 peerDependenciesMeta: '@types/react': optional: true - '@assistant-ui/react@0.11.58': - resolution: {integrity: sha512-5VbparS71X36Q7g+mHwXZvo4eaJohKkQzMP8jBZD9V/Bl26I8s/s3q9WjRqYWMRWaiyYaoEgnQhESM9yyBtW2g==} + '@assistant-ui/react@0.12.19': + resolution: {integrity: sha512-scAf0o8cwjuHT9Y44EFGXcE2y6BSmpeMvt0NxOn8+Y/HBlNttQMLNvrM0p2AjacXCUufagiafAnWybzBV3nKEQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -544,8 +525,18 @@ packages: '@types/react-dom': optional: true - '@assistant-ui/tap@0.3.6': - resolution: {integrity: sha512-4IAN32J9820qbwdc7DeR5HxJVTj+cRPVSMwa9Fv2oP2eMFPAV1eZ8+/co6mgtuM9jSc38vYtZntPsGSHwL7rTg==} + '@assistant-ui/store@0.2.3': + resolution: {integrity: sha512-daStbgSQiX7+csqK6Cvo7A8p8UZkTCSMxBHxbhJvwrlVbp7BRJWTxq3U3rpTkSGIar23SXIyVRRfXU8VW7pswA==} + peerDependencies: + '@assistant-ui/tap': ^0.5.3 + '@types/react': '*' + react: ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@assistant-ui/tap@0.5.3': + resolution: {integrity: sha512-wy06ksqF2LfFxe4JXy31Ns89N/be1Dy3c+mG363cFHFp3CbLkRu8CrCN2SQSgCkXt628E+D8QyzqdBcl9kD4NQ==} peerDependencies: '@types/react': '*' react: ^18 || ^19 @@ -1098,6 +1089,10 @@ packages: resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/standalone@7.29.2': resolution: {integrity: sha512-VSuvywmVRS8efooKrvJzs6BlVSxRvAdLeGrAKUrWoBx1fFBSeE/oBpUZCQ5BcprLyXy04W8skzz7JT8GqlNRJg==} engines: {node: '>=6.9.0'} @@ -4143,10 +4138,6 @@ packages: cpu: [x64] os: [win32] - '@vercel/oidc@3.1.0': - resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} - engines: {node: '>= 20'} - '@xmldom/xmldom@0.8.11': resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} engines: {node: '>=10.0.0'} @@ -4171,12 +4162,6 @@ packages: react: optional: true - ai@6.0.97: - resolution: {integrity: sha512-eZIAcBymwGhBwncRH/v9pillZNMeRCDkc4BwcvwXerXd7sxjVxRis3ZNCNCpP02pVH4NLs81ljm4cElC4vbNcQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} @@ -4227,14 +4212,11 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - assistant-cloud@0.1.18: - resolution: {integrity: sha512-6tq2jPGIBjkjsLQ/Fd4r6PGj4hf05oM2jBl4hBs7YIkaJ3qBVUWiHary2+faNpsPOoY71brsVukl/qz5B1rQkA==} + assistant-cloud@0.1.22: + resolution: {integrity: sha512-AEE9shV+oFrGDv/MRTRERctNKpIYS0n34UpAQXXICiOkSWD6QZnS1ljLqruFko7fJoT5CIWq8dNeJWdzQLTBLg==} - assistant-stream@0.2.47: - resolution: {integrity: sha512-0f+yVwoh7GVwYqaWh6vT+P/zflvEyqysJJzGhjqOPxUYjbNOjcifBw+fVwQPtxysyzye2TZCQtmOWjP0ggvnqw==} - - assistant-stream@0.3.3: - resolution: {integrity: sha512-Ne/uTseMIiZx740dTbr/SWxONM8nYj4Z5BRmUfqQN+TNgtOCgWOlC/oTUQ+A7LIUHtmGbcoyZwDf8yd2RASnDA==} + assistant-stream@0.3.6: + resolution: {integrity: sha512-NdtSRrQfWCDA/aqQ1xhobf/xnhuMZkhFAw9xzAt5iAoL3ouxVXOowSRN87OL4MYBQEvqtcjw9/CE6YcsXoBtuw==} ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -4959,10 +4941,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - eventsource-parser@3.0.6: - resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} - engines: {node: '>=18.0.0'} - extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -6057,6 +6035,11 @@ packages: engines: {node: ^18 || >=20} hasBin: true + nanoid@5.1.7: + resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==} + engines: {node: ^18 || >=20} + hasBin: true + napi-postinstall@0.3.4: resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -7149,6 +7132,11 @@ packages: peerDependencies: react: '>=16.8.0' + use-effect-event@2.0.3: + resolution: {integrity: sha512-fz1en+z3fYXCXx3nMB8hXDMuygBltifNKZq29zDx+xNJ+1vEs6oJlYd9sK31vxJ0YI534VUsHEBY0k2BATsmBQ==} + peerDependencies: + react: ^18.3 || ^19.0.0-0 + use-intl@4.8.3: resolution: {integrity: sha512-nLxlC/RH+le6g3amA508Itnn/00mE+J22ui21QhOWo5V9hCEC43+WtnRAITbJW0ztVZphev5X9gvOf2/Dk9PLA==} peerDependencies: @@ -7331,13 +7319,6 @@ packages: snapshots: - '@ai-sdk/gateway@3.0.53(zod@4.3.6)': - dependencies: - '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) - '@vercel/oidc': 3.1.0 - zod: 4.3.6 - '@ai-sdk/provider-utils@2.2.8(zod@4.3.6)': dependencies: '@ai-sdk/provider': 1.1.3 @@ -7345,21 +7326,10 @@ snapshots: secure-json-parse: 2.7.0 zod: 4.3.6 - '@ai-sdk/provider-utils@4.0.15(zod@4.3.6)': - dependencies: - '@ai-sdk/provider': 3.0.8 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.0.6 - zod: 4.3.6 - '@ai-sdk/provider@1.1.3': dependencies: json-schema: 0.4.0 - '@ai-sdk/provider@3.0.8': - dependencies: - json-schema: 0.4.0 - '@ai-sdk/react@1.2.12(react@19.2.4)(zod@4.3.6)': dependencies: '@ai-sdk/provider-utils': 2.2.8(zod@4.3.6) @@ -7370,16 +7340,6 @@ snapshots: optionalDependencies: zod: 4.3.6 - '@ai-sdk/react@3.0.99(react@19.2.4)(zod@4.3.6)': - dependencies: - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) - ai: 6.0.97(zod@4.3.6) - react: 19.2.4 - swr: 2.4.0(react@19.2.4) - throttleit: 2.1.0 - transitivePeerDependencies: - - zod - '@ai-sdk/ui-utils@1.2.11(zod@4.3.6)': dependencies: '@ai-sdk/provider': 1.1.3 @@ -7405,20 +7365,21 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - '@assistant-ui/react-ai-sdk@1.3.8(@assistant-ui/react@0.11.58(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)))(@types/react@19.2.14)(assistant-cloud@0.1.18)(react@19.2.4)': + '@assistant-ui/core@0.1.7(@assistant-ui/store@0.2.3(@assistant-ui/tap@0.5.3(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@assistant-ui/tap@0.5.3(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(assistant-cloud@0.1.22)(react@19.2.4)(zustand@5.0.11(@types/react@19.2.14)(immer@10.2.0)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)))': dependencies: - '@ai-sdk/react': 3.0.99(react@19.2.4)(zod@4.3.6) - '@assistant-ui/react': 0.11.58(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) - ai: 6.0.97(zod@4.3.6) - react: 19.2.4 - zod: 4.3.6 + '@assistant-ui/store': 0.2.3(@assistant-ui/tap@0.5.3(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4) + '@assistant-ui/tap': 0.5.3(@types/react@19.2.14)(react@19.2.4) + assistant-stream: 0.3.6 + nanoid: 5.1.7 optionalDependencies: '@types/react': 19.2.14 - assistant-cloud: 0.1.18 + assistant-cloud: 0.1.22 + react: 19.2.4 + zustand: 5.0.11(@types/react@19.2.14)(immer@10.2.0)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) - '@assistant-ui/react-markdown@0.11.10(@assistant-ui/react@0.11.58(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)))(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@assistant-ui/react-markdown@0.12.6(@assistant-ui/react@0.12.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)))(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@assistant-ui/react': 0.11.58(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) + '@assistant-ui/react': 0.12.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) classnames: 2.5.1 @@ -7431,21 +7392,21 @@ snapshots: - react-dom - supports-color - '@assistant-ui/react@0.11.58(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))': + '@assistant-ui/react@0.12.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(immer@10.2.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))': dependencies: - '@assistant-ui/tap': 0.3.6(@types/react@19.2.14)(react@19.2.4) + '@assistant-ui/core': 0.1.7(@assistant-ui/store@0.2.3(@assistant-ui/tap@0.5.3(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@assistant-ui/tap@0.5.3(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(assistant-cloud@0.1.22)(react@19.2.4)(zustand@5.0.11(@types/react@19.2.14)(immer@10.2.0)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))) + '@assistant-ui/store': 0.2.3(@assistant-ui/tap@0.5.3(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4) + '@assistant-ui/tap': 0.5.3(@types/react@19.2.14)(react@19.2.4) '@radix-ui/primitive': 1.1.3 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) '@radix-ui/react-context': 1.1.3(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.4) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.4) - assistant-cloud: 0.1.18 - assistant-stream: 0.2.47 - nanoid: 5.1.6 + assistant-cloud: 0.1.22 + assistant-stream: 0.3.6 + nanoid: 5.1.7 + radix-ui: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) react-textarea-autosize: 8.5.9(@types/react@19.2.14)(react@19.2.4) @@ -7458,7 +7419,15 @@ snapshots: - immer - use-sync-external-store - '@assistant-ui/tap@0.3.6(@types/react@19.2.14)(react@19.2.4)': + '@assistant-ui/store@0.2.3(@assistant-ui/tap@0.5.3(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@assistant-ui/tap': 0.5.3(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + use-effect-event: 2.0.3(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + + '@assistant-ui/tap@0.5.3(@types/react@19.2.14)(react@19.2.4)': optionalDependencies: '@types/react': 19.2.14 react: 19.2.4 @@ -8172,6 +8141,8 @@ snapshots: '@babel/runtime@7.28.6': {} + '@babel/runtime@7.29.2': {} + '@babel/standalone@7.29.2': {} '@babel/template@7.28.6': @@ -11052,8 +11023,6 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vercel/oidc@3.1.0': {} - '@xmldom/xmldom@0.8.11': {} acorn-jsx@5.3.2(acorn@8.16.0): @@ -11074,14 +11043,6 @@ snapshots: optionalDependencies: react: 19.2.4 - ai@6.0.97(zod@4.3.6): - dependencies: - '@ai-sdk/gateway': 3.0.53(zod@4.3.6) - '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) - '@opentelemetry/api': 1.9.0 - zod: 4.3.6 - ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 @@ -11168,20 +11129,14 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 - assistant-cloud@0.1.18: + assistant-cloud@0.1.22: dependencies: - assistant-stream: 0.3.3 + assistant-stream: 0.3.6 - assistant-stream@0.2.47: + assistant-stream@0.3.6: dependencies: '@standard-schema/spec': 1.1.0 - nanoid: 5.1.6 - secure-json-parse: 4.1.0 - - assistant-stream@0.3.3: - dependencies: - '@standard-schema/spec': 1.1.0 - nanoid: 5.1.6 + nanoid: 5.1.7 secure-json-parse: 4.1.0 ast-types-flow@0.0.8: {} @@ -12054,8 +12009,6 @@ snapshots: esutils@2.0.3: {} - eventsource-parser@3.0.6: {} - extend@3.0.2: {} fast-deep-equal@3.1.3: {} @@ -13437,6 +13390,8 @@ snapshots: nanoid@5.1.6: {} + nanoid@5.1.7: {} + napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} @@ -14027,7 +13982,7 @@ snapshots: react-textarea-autosize@8.5.9(@types/react@19.2.14)(react@19.2.4): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 react: 19.2.4 use-composed-ref: 1.4.0(@types/react@19.2.14)(react@19.2.4) use-latest: 1.3.0(@types/react@19.2.14)(react@19.2.4) @@ -14884,6 +14839,10 @@ snapshots: dequal: 2.0.3 react: 19.2.4 + use-effect-event@2.0.3(react@19.2.4): + dependencies: + react: 19.2.4 + use-intl@4.8.3(react@19.2.4): dependencies: '@formatjs/fast-memoize': 3.1.0