-
+
Loading your chats...
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/AIChatBubble/AIChatBubble.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/AIChatBubble/AIChatBubble.tsx
new file mode 100644
index 0000000000..64c2aadbae
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/AIChatBubble/AIChatBubble.tsx
@@ -0,0 +1,20 @@
+import { cn } from "@/lib/utils";
+import { ReactNode } from "react";
+
+export interface AIChatBubbleProps {
+ children: ReactNode;
+ className?: string;
+}
+
+export function AIChatBubble({ children, className }: AIChatBubbleProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/AgentCarouselMessage/AgentCarouselMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/AgentCarouselMessage/AgentCarouselMessage.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/AgentCarouselMessage/AgentCarouselMessage.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/AgentCarouselMessage/AgentCarouselMessage.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/AgentInputsSetup/AgentInputsSetup.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/AgentInputsSetup/AgentInputsSetup.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/AgentInputsSetup/AgentInputsSetup.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/AgentInputsSetup/AgentInputsSetup.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/AgentInputsSetup/useAgentInputsSetup.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/AgentInputsSetup/useAgentInputsSetup.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/AgentInputsSetup/useAgentInputsSetup.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/AgentInputsSetup/useAgentInputsSetup.ts
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/AuthPromptWidget/AuthPromptWidget.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/AuthPromptWidget/AuthPromptWidget.tsx
new file mode 100644
index 0000000000..b2cf92ec56
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/AuthPromptWidget/AuthPromptWidget.tsx
@@ -0,0 +1,120 @@
+"use client";
+
+import { Button } from "@/components/atoms/Button/Button";
+import { cn } from "@/lib/utils";
+import { ShieldIcon, SignInIcon, UserPlusIcon } from "@phosphor-icons/react";
+import { useRouter } from "next/navigation";
+
+export interface AuthPromptWidgetProps {
+ message: string;
+ sessionId: string;
+ agentInfo?: {
+ graph_id: string;
+ name: string;
+ trigger_type: string;
+ };
+ returnUrl?: string;
+ className?: string;
+}
+
+export function AuthPromptWidget({
+ message,
+ sessionId,
+ agentInfo,
+ returnUrl = "/copilot/chat",
+ className,
+}: AuthPromptWidgetProps) {
+ const router = useRouter();
+
+ function handleSignIn() {
+ if (typeof window !== "undefined") {
+ localStorage.setItem("pending_chat_session", sessionId);
+ if (agentInfo) {
+ localStorage.setItem("pending_agent_setup", JSON.stringify(agentInfo));
+ }
+ }
+ const returnUrlWithSession = `${returnUrl}?session_id=${sessionId}`;
+ const encodedReturnUrl = encodeURIComponent(returnUrlWithSession);
+ router.push(`/login?returnUrl=${encodedReturnUrl}`);
+ }
+
+ function handleSignUp() {
+ if (typeof window !== "undefined") {
+ localStorage.setItem("pending_chat_session", sessionId);
+ if (agentInfo) {
+ localStorage.setItem("pending_agent_setup", JSON.stringify(agentInfo));
+ }
+ }
+ const returnUrlWithSession = `${returnUrl}?session_id=${sessionId}`;
+ const encodedReturnUrl = encodeURIComponent(returnUrlWithSession);
+ router.push(`/signup?returnUrl=${encodedReturnUrl}`);
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ Authentication Required
+
+
+ Sign in to set up and manage agents
+
+
+
+
+
+
{message}
+ {agentInfo && (
+
+
+ Ready to set up:{" "}
+ {agentInfo.name}
+
+
+ Type:{" "}
+ {agentInfo.trigger_type}
+
+
+ )}
+
+
+
+
+
+
+
+
+ Your chat session will be preserved after signing in
+
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/ChatContainer.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/ChatContainer.tsx
index bca566b43e..a838bf3d78 100644
--- a/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/ChatContainer.tsx
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/ChatContainer.tsx
@@ -50,11 +50,11 @@ export function ChatContainer({
return (
{/* Messages or Welcome Screen - Scrollable */}
-
-
+
+
{/* Input - Fixed at bottom */}
-
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/createStreamEventDispatcher.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/createStreamEventDispatcher.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/createStreamEventDispatcher.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/createStreamEventDispatcher.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/helpers.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/helpers.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/helpers.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/helpers.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/useChatContainer.handlers.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/useChatContainer.handlers.ts
similarity index 92%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/useChatContainer.handlers.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/useChatContainer.handlers.ts
index 064b847064..e724c4c61b 100644
--- a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/useChatContainer.handlers.ts
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/useChatContainer.handlers.ts
@@ -33,13 +33,22 @@ export function handleTextEnded(
console.log("[Text Ended] Saving streamed text as assistant message");
const completedText = deps.streamingChunksRef.current.join("");
if (completedText.trim()) {
- const assistantMessage: ChatMessageData = {
- type: "message",
- role: "assistant",
- content: completedText,
- timestamp: new Date(),
- };
- deps.setMessages((prev) => [...prev, assistantMessage]);
+ deps.setMessages((prev) => {
+ const lastMessage = prev[prev.length - 1];
+ console.log("[Text Ended] Previous message:", {
+ type: lastMessage?.type,
+ toolName: lastMessage?.type === "tool_call" ? lastMessage.toolName : undefined,
+ content: completedText.substring(0, 200),
+ });
+
+ const assistantMessage: ChatMessageData = {
+ type: "message",
+ role: "assistant",
+ content: completedText,
+ timestamp: new Date(),
+ };
+ return [...prev, assistantMessage];
+ });
}
deps.setStreamingChunks([]);
deps.streamingChunksRef.current = [];
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/useChatContainer.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/useChatContainer.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatContainer/useChatContainer.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/useChatContainer.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatCredentialsSetup/ChatCredentialsSetup.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatCredentialsSetup/ChatCredentialsSetup.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatCredentialsSetup/ChatCredentialsSetup.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatCredentialsSetup/ChatCredentialsSetup.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatCredentialsSetup/useChatCredentialsSetup.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatCredentialsSetup/useChatCredentialsSetup.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatCredentialsSetup/useChatCredentialsSetup.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatCredentialsSetup/useChatCredentialsSetup.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatErrorState/ChatErrorState.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatErrorState/ChatErrorState.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatErrorState/ChatErrorState.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatErrorState/ChatErrorState.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatInput/ChatInput.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatInput/ChatInput.tsx
similarity index 97%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatInput/ChatInput.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatInput/ChatInput.tsx
index 3101174a11..390c8335a6 100644
--- a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatInput/ChatInput.tsx
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatInput/ChatInput.tsx
@@ -3,7 +3,7 @@ import { cn } from "@/lib/utils";
import { ArrowUpIcon } from "@phosphor-icons/react";
import { useChatInput } from "./useChatInput";
-export interface ChatInputProps {
+export interface Props {
onSend: (message: string) => void;
disabled?: boolean;
placeholder?: string;
@@ -15,7 +15,7 @@ export function ChatInput({
disabled = false,
placeholder = "Type your message...",
className,
-}: ChatInputProps) {
+}: Props) {
const inputId = "chat-input";
const { value, setValue, handleKeyDown, handleSend } = useChatInput({
onSend,
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatInput/useChatInput.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatInput/useChatInput.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatInput/useChatInput.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatInput/useChatInput.ts
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatLoader/ChatLoader.module.css b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatLoader/ChatLoader.module.css
new file mode 100644
index 0000000000..34ad7d6dd6
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatLoader/ChatLoader.module.css
@@ -0,0 +1,11 @@
+.loader {
+ width: 20px;
+ aspect-ratio: 1;
+ border-radius: 50%;
+ background: #000;
+ box-shadow: 0 0 0 0 #0004;
+ animation: l1 1s infinite;
+}
+@keyframes l1 {
+ 100% {box-shadow: 0 0 0 30px #0000}
+}
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatLoader/ChatLoader.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatLoader/ChatLoader.tsx
new file mode 100644
index 0000000000..65d32f310a
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatLoader/ChatLoader.tsx
@@ -0,0 +1,5 @@
+import styles from "./ChatLoader.module.css";
+
+export function ChatLoader() {
+ return ;
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatLoadingState/ChatLoadingState.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatLoadingState/ChatLoadingState.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatLoadingState/ChatLoadingState.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatLoadingState/ChatLoadingState.tsx
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatMessage/ChatMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatMessage/ChatMessage.tsx
new file mode 100644
index 0000000000..d30dbf6edb
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatMessage/ChatMessage.tsx
@@ -0,0 +1,299 @@
+"use client";
+
+import { Button } from "@/components/atoms/Button/Button";
+import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
+import { cn } from "@/lib/utils";
+import {
+ ArrowClockwise,
+ CheckCircleIcon,
+ CheckIcon,
+ CopyIcon,
+} from "@phosphor-icons/react";
+import { useRouter } from "next/navigation";
+import { useCallback, useState } from "react";
+import { AgentCarouselMessage } from "../AgentCarouselMessage/AgentCarouselMessage";
+import { AIChatBubble } from "../AIChatBubble/AIChatBubble";
+import { AuthPromptWidget } from "../AuthPromptWidget/AuthPromptWidget";
+import { ChatCredentialsSetup } from "../ChatCredentialsSetup/ChatCredentialsSetup";
+import { ExecutionStartedMessage } from "../ExecutionStartedMessage/ExecutionStartedMessage";
+import { MarkdownContent } from "../MarkdownContent/MarkdownContent";
+import { NoResultsMessage } from "../NoResultsMessage/NoResultsMessage";
+import { ToolCallMessage } from "../ToolCallMessage/ToolCallMessage";
+import { ToolResponseMessage } from "../ToolResponseMessage/ToolResponseMessage";
+import { UserChatBubble } from "../UserChatBubble/UserChatBubble";
+import { useChatMessage, type ChatMessageData } from "./useChatMessage";
+export interface ChatMessageProps {
+ message: ChatMessageData;
+ className?: string;
+ onDismissLogin?: () => void;
+ onDismissCredentials?: () => void;
+ onSendMessage?: (content: string, isUserMessage?: boolean) => void;
+ agentOutput?: ChatMessageData;
+ isFinalMessage?: boolean;
+}
+
+export function ChatMessage({
+ message,
+ className,
+ onDismissCredentials,
+ onSendMessage,
+ agentOutput,
+ isFinalMessage = true,
+}: ChatMessageProps) {
+ const { user } = useSupabase();
+ const router = useRouter();
+ const [copied, setCopied] = useState(false);
+ const {
+ isUser,
+ isToolCall,
+ isToolResponse,
+ isLoginNeeded,
+ isCredentialsNeeded,
+ } = useChatMessage(message);
+
+ const handleAllCredentialsComplete = useCallback(
+ function handleAllCredentialsComplete() {
+ // Send a user message that explicitly asks to retry the setup
+ // This ensures the LLM calls get_required_setup_info again and proceeds with execution
+ if (onSendMessage) {
+ onSendMessage(
+ "I've configured the required credentials. Please check if everything is ready and proceed with setting up the agent.",
+ );
+ }
+ // Optionally dismiss the credentials prompt
+ if (onDismissCredentials) {
+ onDismissCredentials();
+ }
+ },
+ [onSendMessage, onDismissCredentials],
+ );
+
+ function handleCancelCredentials() {
+ // Dismiss the credentials prompt
+ if (onDismissCredentials) {
+ onDismissCredentials();
+ }
+ }
+
+ const handleCopy = useCallback(async () => {
+ if (message.type !== "message") return;
+
+ try {
+ await navigator.clipboard.writeText(message.content);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } catch (error) {
+ console.error("Failed to copy:", error);
+ }
+ }, [message]);
+
+ const handleTryAgain = useCallback(() => {
+ if (message.type !== "message" || !onSendMessage) return;
+ onSendMessage(message.content, message.role === "user");
+ }, [message, onSendMessage]);
+
+ const handleViewExecution = useCallback(() => {
+ if (message.type === "execution_started" && message.libraryAgentLink) {
+ router.push(message.libraryAgentLink);
+ }
+ }, [message, router]);
+
+ // Render credentials needed messages
+ if (isCredentialsNeeded && message.type === "credentials_needed") {
+ return (
+
+ );
+ }
+
+ // Render login needed messages
+ if (isLoginNeeded && message.type === "login_needed") {
+ // If user is already logged in, show success message instead of auth prompt
+ if (user) {
+ return (
+
+
+
+
+
+
+
+
+
+ Successfully Authenticated
+
+
+ You're now signed in and ready to continue
+
+
+
+
+
+
+ );
+ }
+
+ // Show auth prompt if not logged in
+ return (
+
+ );
+ }
+
+ // Render tool call messages
+ if (isToolCall && message.type === "tool_call") {
+ return (
+
+
+
+ );
+ }
+
+ // Render no_results messages - use dedicated component, not ToolResponseMessage
+ if (message.type === "no_results") {
+ return (
+
+
+
+ );
+ }
+
+ // Render agent_carousel messages - use dedicated component, not ToolResponseMessage
+ if (message.type === "agent_carousel") {
+ return (
+
+ );
+ }
+
+ // Render execution_started messages - use dedicated component, not ToolResponseMessage
+ if (message.type === "execution_started") {
+ return (
+
+
+
+ );
+ }
+
+ // Render tool response messages (but skip agent_output if it's being rendered inside assistant message)
+ if (isToolResponse && message.type === "tool_response") {
+ return (
+
+
+
+ );
+ }
+
+ // Render regular chat messages
+ if (message.type === "message") {
+ return (
+
+
+
+ {isUser ? (
+
+
+
+ ) : (
+
+
+ {agentOutput &&
+ agentOutput.type === "tool_response" && (
+
+
+
+ )}
+
+ )}
+
+ {isUser && onSendMessage && (
+
+ )}
+ {(isUser || isFinalMessage) && (
+
+ )}
+
+
+
+
+ );
+ }
+
+ // Fallback for unknown message types
+ return null;
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatMessage/useChatMessage.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatMessage/useChatMessage.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ChatMessage/useChatMessage.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ChatMessage/useChatMessage.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ExecutionStartedMessage/ExecutionStartedMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ExecutionStartedMessage/ExecutionStartedMessage.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ExecutionStartedMessage/ExecutionStartedMessage.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ExecutionStartedMessage/ExecutionStartedMessage.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/MarkdownContent/MarkdownContent.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/MarkdownContent/MarkdownContent.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/MarkdownContent/MarkdownContent.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/MarkdownContent/MarkdownContent.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/MessageBubble/MessageBubble.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/MessageBubble/MessageBubble.tsx
similarity index 83%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/MessageBubble/MessageBubble.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/MessageBubble/MessageBubble.tsx
index 98b50f3d28..fa3a6847a3 100644
--- a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/MessageBubble/MessageBubble.tsx
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/MessageBubble/MessageBubble.tsx
@@ -13,10 +13,9 @@ export function MessageBubble({
className,
}: MessageBubbleProps) {
const userTheme = {
- bg: "bg-slate-900",
- border: "border-slate-800",
- gradient: "from-slate-900/30 via-slate-800/20 to-transparent",
- text: "text-slate-50",
+ bg: "bg-purple-100",
+ border: "border-purple-100",
+ text: "text-slate-900",
};
const assistantTheme = {
@@ -41,7 +40,7 @@ export function MessageBubble({
>
{/* Gradient flare background */}
void;
+ onSendMessage?: (content: string) => void;
+}
+
+export function MessageList({
+ messages,
+ streamingChunks = [],
+ isStreaming = false,
+ className,
+ onStreamComplete,
+ onSendMessage,
+}: MessageListProps) {
+ const { messagesEndRef, messagesContainerRef } = useMessageList({
+ messageCount: messages.length,
+ isStreaming,
+ });
+
+ return (
+
+
+ {/* Render all persisted messages */}
+ {(() => {
+ let lastAssistantMessageIndex = -1;
+ for (let i = messages.length - 1; i >= 0; i--) {
+ const msg = messages[i];
+ if (msg.type === "message" && msg.role === "assistant") {
+ lastAssistantMessageIndex = i;
+ break;
+ }
+ }
+
+ let lastToolResponseIndex = -1;
+ for (let i = messages.length - 1; i >= 0; i--) {
+ const msg = messages[i];
+ if (msg.type === "tool_response") {
+ lastToolResponseIndex = i;
+ break;
+ }
+ }
+
+ return messages
+ .map((message, index) => {
+ // Log message for debugging
+ if (message.type === "message" && message.role === "assistant") {
+ const prevMessage = messages[index - 1];
+ const prevMessageToolName = prevMessage?.type === "tool_call" ? prevMessage.toolName : undefined;
+ console.log("[MessageList] Assistant message:", {
+ index,
+ content: message.content.substring(0, 200),
+ fullContent: message.content,
+ prevMessageType: prevMessage?.type,
+ prevMessageToolName,
+ });
+ }
+
+ // Check if current message is an agent_output tool_response
+ // and if previous message is an assistant message
+ let agentOutput: ChatMessageData | undefined;
+ let messageToRender: ChatMessageData = message;
+
+ if (message.type === "tool_response" && message.result) {
+ let parsedResult: Record
| null = null;
+ try {
+ parsedResult =
+ typeof message.result === "string"
+ ? JSON.parse(message.result)
+ : (message.result as Record);
+ } catch {
+ parsedResult = null;
+ }
+ if (parsedResult?.type === "agent_output") {
+ const prevMessage = messages[index - 1];
+ if (
+ prevMessage &&
+ prevMessage.type === "message" &&
+ prevMessage.role === "assistant"
+ ) {
+ // This agent output will be rendered inside the previous assistant message
+ // Skip rendering this message separately
+ return null;
+ }
+ }
+ }
+
+ // Check if assistant message follows a tool_call and looks like a tool output
+ if (message.type === "message" && message.role === "assistant") {
+ const prevMessage = messages[index - 1];
+
+ // Check if next message is an agent_output tool_response to include in current assistant message
+ const nextMessage = messages[index + 1];
+ if (
+ nextMessage &&
+ nextMessage.type === "tool_response" &&
+ nextMessage.result
+ ) {
+ let parsedResult: Record | null = null;
+ try {
+ parsedResult =
+ typeof nextMessage.result === "string"
+ ? JSON.parse(nextMessage.result)
+ : (nextMessage.result as Record);
+ } catch {
+ parsedResult = null;
+ }
+ if (parsedResult?.type === "agent_output") {
+ agentOutput = nextMessage;
+ }
+ }
+
+ // Only convert to tool_response if it follows a tool_call AND looks like a tool output
+ if (prevMessage && prevMessage.type === "tool_call") {
+ const content = message.content.toLowerCase().trim();
+ // Patterns that indicate this is a tool output result, not an agent response
+ const isToolOutputPattern =
+ content.startsWith("no agents found") ||
+ content.startsWith("no results found") ||
+ content.includes("no agents found matching") ||
+ content.match(/^no \w+ found/i) ||
+ (content.length < 150 && content.includes("try different")) ||
+ (content.length < 200 && !content.includes("i'll") && !content.includes("let me") && !content.includes("i can") && !content.includes("i will"));
+
+ console.log("[MessageList] Checking if assistant message is tool output:", {
+ content: message.content.substring(0, 100),
+ isToolOutputPattern,
+ prevToolName: prevMessage.toolName,
+ });
+
+ if (isToolOutputPattern) {
+ // Convert this message to a tool_response format for rendering
+ messageToRender = {
+ type: "tool_response",
+ toolId: prevMessage.toolId,
+ toolName: prevMessage.toolName,
+ result: message.content,
+ success: true,
+ timestamp: message.timestamp,
+ } as ChatMessageData;
+ }
+ }
+ }
+
+ const isFinalMessage =
+ messageToRender.type !== "message" ||
+ messageToRender.role !== "assistant" ||
+ index === lastAssistantMessageIndex;
+
+ // Render last tool_response as AIChatBubble (but skip agent_output that's rendered inside assistant message)
+ if (
+ messageToRender.type === "tool_response" &&
+ message.type === "tool_response" &&
+ index === lastToolResponseIndex
+ ) {
+ // Check if this is an agent_output that should be rendered inside assistant message
+ let parsedResult: Record | null = null;
+ try {
+ parsedResult =
+ typeof messageToRender.result === "string"
+ ? JSON.parse(messageToRender.result)
+ : (messageToRender.result as Record);
+ } catch {
+ parsedResult = null;
+ }
+
+ const isAgentOutput = parsedResult?.type === "agent_output";
+ const prevMessage = messages[index - 1];
+ const shouldSkip =
+ isAgentOutput &&
+ prevMessage &&
+ prevMessage.type === "message" &&
+ prevMessage.role === "assistant";
+
+ if (shouldSkip) return null;
+
+ const resultValue =
+ typeof messageToRender.result === "string"
+ ? messageToRender.result
+ : messageToRender.result
+ ? JSON.stringify(messageToRender.result, null, 2)
+ : "";
+
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ });
+ })()}
+
+ {/* Render thinking message when streaming but no chunks yet */}
+ {isStreaming && streamingChunks.length === 0 && }
+
+ {/* Render streaming message if active */}
+ {isStreaming && streamingChunks.length > 0 && (
+
+ )}
+
+ {/* Invisible div to scroll to */}
+
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/MessageList/useMessageList.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/MessageList/useMessageList.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/MessageList/useMessageList.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/MessageList/useMessageList.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/NoResultsMessage/NoResultsMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/NoResultsMessage/NoResultsMessage.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/NoResultsMessage/NoResultsMessage.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/NoResultsMessage/NoResultsMessage.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/QuickActionsWelcome/QuickActionsWelcome.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/QuickActionsWelcome/QuickActionsWelcome.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/QuickActionsWelcome/QuickActionsWelcome.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/QuickActionsWelcome/QuickActionsWelcome.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/SessionsDrawer/SessionsDrawer.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/SessionsDrawer/SessionsDrawer.tsx
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/SessionsDrawer/SessionsDrawer.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/SessionsDrawer/SessionsDrawer.tsx
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/StreamingMessage/StreamingMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/StreamingMessage/StreamingMessage.tsx
similarity index 65%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/StreamingMessage/StreamingMessage.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/StreamingMessage/StreamingMessage.tsx
index 2a6e3d5822..5b1f8b1617 100644
--- a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/StreamingMessage/StreamingMessage.tsx
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/StreamingMessage/StreamingMessage.tsx
@@ -1,7 +1,6 @@
import { cn } from "@/lib/utils";
-import { RobotIcon } from "@phosphor-icons/react";
+import { AIChatBubble } from "../AIChatBubble/AIChatBubble";
import { MarkdownContent } from "../MarkdownContent/MarkdownContent";
-import { MessageBubble } from "../MessageBubble/MessageBubble";
import { useStreamingMessage } from "./useStreamingMessage";
export interface StreamingMessageProps {
@@ -25,16 +24,10 @@ export function StreamingMessage({
)}
>
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/StreamingMessage/useStreamingMessage.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/StreamingMessage/useStreamingMessage.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/StreamingMessage/useStreamingMessage.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/StreamingMessage/useStreamingMessage.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ThinkingMessage/ThinkingMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ThinkingMessage/ThinkingMessage.tsx
similarity index 81%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ThinkingMessage/ThinkingMessage.tsx
rename to autogpt_platform/frontend/src/components/contextual/Chat/components/ThinkingMessage/ThinkingMessage.tsx
index d8adddf416..15ed6f495f 100644
--- a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/components/ThinkingMessage/ThinkingMessage.tsx
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ThinkingMessage/ThinkingMessage.tsx
@@ -1,7 +1,6 @@
import { cn } from "@/lib/utils";
-import { RobotIcon } from "@phosphor-icons/react";
import { useEffect, useRef, useState } from "react";
-import { MessageBubble } from "../MessageBubble/MessageBubble";
+import { AIChatBubble } from "../AIChatBubble/AIChatBubble";
export interface ThinkingMessageProps {
className?: string;
@@ -34,14 +33,8 @@ export function ThinkingMessage({ className }: ThinkingMessageProps) {
)}
>
-
-
-
+
{showSlowLoader ? (
@@ -62,7 +55,7 @@ export function ThinkingMessage({ className }: ThinkingMessageProps) {
)}
-
+
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolCallMessage/ToolCallMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolCallMessage/ToolCallMessage.tsx
new file mode 100644
index 0000000000..fb9574660c
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolCallMessage/ToolCallMessage.tsx
@@ -0,0 +1,33 @@
+import { Text } from "@/components/atoms/Text/Text";
+import type { ToolArguments } from "@/types/chat";
+import { AIChatBubble } from "../AIChatBubble/AIChatBubble";
+
+export interface ToolCallMessageProps {
+ toolId?: string;
+ toolName: string;
+ arguments?: ToolArguments;
+ className?: string;
+}
+
+export function ToolCallMessage({
+ toolId,
+ toolName,
+ arguments: toolArguments,
+ className,
+}: ToolCallMessageProps) {
+ const displayKey = toolName || toolId;
+
+ const displayData = toolArguments
+ ? JSON.stringify(toolArguments)
+ : "No arguments";
+
+ const displayText = `${displayKey}: ${displayData}`;
+
+ return (
+
+
+ {displayText}
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolResponseMessage/ToolResponseMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolResponseMessage/ToolResponseMessage.tsx
new file mode 100644
index 0000000000..01829ffa5c
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolResponseMessage/ToolResponseMessage.tsx
@@ -0,0 +1,36 @@
+import { Text } from "@/components/atoms/Text/Text";
+import type { ToolResult } from "@/types/chat";
+import { AIChatBubble } from "../AIChatBubble/AIChatBubble";
+
+export interface ToolResponseMessageProps {
+ toolId?: string;
+ toolName: string;
+ result?: ToolResult;
+ success?: boolean;
+ className?: string;
+}
+
+export function ToolResponseMessage({
+ toolId,
+ toolName,
+ result: _result,
+ success: _success = true,
+ className,
+}: ToolResponseMessageProps) {
+ const displayKey = toolId || toolName;
+ const resultValue =
+ typeof _result === "string"
+ ? _result
+ : _result
+ ? JSON.stringify(_result)
+ : toolName;
+ const displayText = `${displayKey}: ${resultValue}`;
+
+ return (
+
+
+ {displayText}
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/UserChatBubble/UserChatBubble.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/UserChatBubble/UserChatBubble.tsx
new file mode 100644
index 0000000000..34d888f746
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/UserChatBubble/UserChatBubble.tsx
@@ -0,0 +1,28 @@
+import { cn } from "@/lib/utils";
+import { ReactNode } from "react";
+
+export interface UserChatBubbleProps {
+ children: ReactNode;
+ className?: string;
+}
+
+export function UserChatBubble({
+ children,
+ className,
+}: UserChatBubbleProps) {
+ return (
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/helpers.ts b/autogpt_platform/frontend/src/components/contextual/Chat/helpers.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/helpers.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/helpers.ts
diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/useChat.ts b/autogpt_platform/frontend/src/components/contextual/Chat/useChat.ts
new file mode 100644
index 0000000000..9182fa922d
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/contextual/Chat/useChat.ts
@@ -0,0 +1,120 @@
+"use client";
+
+import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
+import { useEffect, useRef, useState } from "react";
+import { toast } from "sonner";
+import { useChatSession } from "./useChatSession";
+import { useChatStream } from "./useChatStream";
+
+interface UseChatArgs {
+ urlSessionId?: string | null;
+}
+
+export function useChat({ urlSessionId }: UseChatArgs = {}) {
+ const hasClaimedSessionRef = useRef(false);
+ const { user } = useSupabase();
+ const { sendMessage: sendStreamMessage } = useChatStream();
+ const [showLoader, setShowLoader] = useState(false);
+ const {
+ session,
+ sessionId: sessionIdFromHook,
+ messages,
+ isLoading,
+ isCreating,
+ error,
+ createSession,
+ claimSession,
+ clearSession: clearSessionBase,
+ loadSession,
+ } = useChatSession({
+ urlSessionId,
+ autoCreate: false,
+ });
+
+ useEffect(
+ function autoClaimSession() {
+ if (
+ session &&
+ !session.user_id &&
+ user &&
+ !hasClaimedSessionRef.current &&
+ !isLoading &&
+ sessionIdFromHook
+ ) {
+ hasClaimedSessionRef.current = true;
+ claimSession(sessionIdFromHook)
+ .then(() => {
+ sendStreamMessage(
+ sessionIdFromHook,
+ "User has successfully logged in.",
+ () => {},
+ false,
+ ).catch(() => {});
+ })
+ .catch(() => {
+ hasClaimedSessionRef.current = false;
+ });
+ }
+ },
+ [
+ session,
+ user,
+ isLoading,
+ sessionIdFromHook,
+ claimSession,
+ sendStreamMessage,
+ ],
+ );
+
+
+ useEffect(() => {
+ if (isLoading || isCreating) {
+ const timer = setTimeout(() => {
+ setShowLoader(true);
+ }, 300);
+ return () => clearTimeout(timer);
+ } else {
+ setShowLoader(false);
+ }
+ }, [isLoading, isCreating]);
+
+ useEffect(function monitorNetworkStatus() {
+ function handleOnline() {
+ toast.success("Connection restored", {
+ description: "You're back online",
+ });
+ }
+
+ function handleOffline() {
+ toast.error("You're offline", {
+ description: "Check your internet connection",
+ });
+ }
+
+ window.addEventListener("online", handleOnline);
+ window.addEventListener("offline", handleOffline);
+
+ return () => {
+ window.removeEventListener("online", handleOnline);
+ window.removeEventListener("offline", handleOffline);
+ };
+ }, []);
+
+ function clearSession() {
+ clearSessionBase();
+ hasClaimedSessionRef.current = false;
+ }
+
+ return {
+ session,
+ messages,
+ isLoading,
+ isCreating,
+ error,
+ createSession,
+ clearSession,
+ loadSession,
+ sessionId: sessionIdFromHook,
+ showLoader,
+ };
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/useChatDrawer.ts b/autogpt_platform/frontend/src/components/contextual/Chat/useChatDrawer.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/useChatDrawer.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/useChatDrawer.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/useChatSession.ts b/autogpt_platform/frontend/src/components/contextual/Chat/useChatSession.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/useChatSession.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/useChatSession.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/useChatStream.ts b/autogpt_platform/frontend/src/components/contextual/Chat/useChatStream.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/useChatStream.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/useChatStream.ts
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/usePageContext.ts b/autogpt_platform/frontend/src/components/contextual/Chat/usePageContext.ts
similarity index 100%
rename from autogpt_platform/frontend/src/app/(platform)/chat/components/Chat/usePageContext.ts
rename to autogpt_platform/frontend/src/components/contextual/Chat/usePageContext.ts