diff --git a/apps/web/src/app/dashboard/agents/[id]/chat/[chatId]/page.tsx b/apps/web/src/app/dashboard/agents/[id]/chat/[chatId]/page.tsx index 99237e6..61b496e 100644 --- a/apps/web/src/app/dashboard/agents/[id]/chat/[chatId]/page.tsx +++ b/apps/web/src/app/dashboard/agents/[id]/chat/[chatId]/page.tsx @@ -1,6 +1,7 @@ 'use client'; import type { AIProvider } from '@tpmjs/types/agent'; +import { Badge } from '@tpmjs/ui/Badge/Badge'; import { Button } from '@tpmjs/ui/Button/Button'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import { Textarea } from '@tpmjs/ui/Textarea/Textarea'; @@ -8,6 +9,11 @@ import Link from 'next/link'; import { useParams, useRouter } from 'next/navigation'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Streamdown } from 'streamdown'; +import type { AgentSettings } from '~/components/agents/ChatSettingsDrawer'; +import { ChatSettingsDrawer } from '~/components/agents/ChatSettingsDrawer'; +import type { CollectionInfo, ToolInfo } from '~/components/agents/ChatToolsPanel'; +import { ChatToolsPanel } from '~/components/agents/ChatToolsPanel'; +import { ToolDetailsModal } from '~/components/agents/ToolDetailsModal'; import { DashboardLayout } from '~/components/dashboard/DashboardLayout'; interface Agent { @@ -17,6 +23,12 @@ interface Agent { description: string | null; provider: AIProvider; modelId: string; + systemPrompt: string | null; + temperature: number; + maxToolCallsPerTurn: number; + maxMessagesInContext: number; + tools: ToolInfo[]; + collections: CollectionInfo[]; } interface MessageToolCall { @@ -71,7 +83,7 @@ function getErrorMessage(output: unknown): string | null { } /** - * Sexy tool call debug card component + * Tool call card with style guide aesthetics */ function ToolCallCard({ toolCall, @@ -82,7 +94,6 @@ function ToolCallCard({ isExpanded: boolean; onToggle: () => void; }) { - // Detect if the output contains an error const hasError = Boolean(toolCall.output && isToolError(toolCall.output)); const errorMessage = hasError ? getErrorMessage(toolCall.output) : null; const effectiveStatus = hasError ? 'error' : toolCall.status; @@ -110,10 +121,12 @@ function ToolCallCard({ }; return ( -
- {/* Header */} + {/* header */}
-
+
- {toolCall.toolName} + {toolCall.toolName} {toolCall.toolCallId.slice(0, 8)}... {hasError && ( - - ERROR + + error )}
- {/* Show error message preview in header */} {hasError && errorMessage && !isExpanded && ( -
- {errorMessage} -
+
{errorMessage}
)}
- {/* Expanded Content */} + {/* expanded content */} {isExpanded && ( -
- {/* Input Section */} +
+ {/* input */} {toolCall.input !== undefined && toolCall.input !== null ? ( -
+
- Input + input
@@ -170,25 +180,25 @@ function ToolCallCard({
) : null} - {/* Error Message Section */} + {/* error message */} {hasError && errorMessage && ( -
+
- Error + error

{errorMessage}

)} - {/* Output Section */} + {/* output */} {toolCall.output !== undefined && toolCall.output !== null ? (
- {hasError ? 'Full Response' : 'Output'} + {hasError ? 'full response' : 'output'}
@@ -200,16 +210,16 @@ function ToolCallCard({
) : null} - {/* Status indicator for running */} + {/* running indicator */} {toolCall.status === 'running' && !toolCall.output && (
- Executing... + executing...
)}
)} -
+ ); } @@ -241,7 +251,6 @@ export default function AgentChatPage(): React.ReactElement { const [agent, setAgent] = useState(null); const [conversations, setConversations] = useState([]); - // Use chatId from URL as the active conversation const activeConversationId = chatId; const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); @@ -252,6 +261,11 @@ export default function AgentChatPage(): React.ReactElement { const [toolCalls, setToolCalls] = useState([]); const [expandedToolCalls, setExpandedToolCalls] = useState>(new Set()); + // UI state + const [showToolsPanel, setShowToolsPanel] = useState(true); + const [showSettings, setShowSettings] = useState(false); + const [selectedTool, setSelectedTool] = useState(null); + const messagesEndRef = useRef(null); const inputRef = useRef(null); @@ -314,11 +328,6 @@ export default function AgentChatPage(): React.ReactElement { if (data.success) { const msgs = data.data.messages || []; - console.log( - '[fetchMessages] Received messages:', - msgs.length, - msgs.map((m: Message) => ({ role: m.role, toolName: m.toolName })) - ); setMessages(msgs); } } catch (err) { @@ -352,7 +361,6 @@ export default function AgentChatPage(): React.ReactElement { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }); - // Navigate to conversation when selected from sidebar const handleSelectConversation = (convSlug: string) => { router.push(`/dashboard/agents/${agentId}/chat/${convSlug}`); }; @@ -367,7 +375,6 @@ export default function AgentChatPage(): React.ReactElement { setError(null); setToolCalls([]); - // Use the chatId from URL const conversationId = activeConversationId || chatId; // Optimistically add user message @@ -406,7 +413,7 @@ export default function AgentChatPage(): React.ReactElement { // Parse SSE events const lines = buffer.split('\n'); - buffer = lines.pop() || ''; // Keep incomplete line in buffer + buffer = lines.pop() || ''; let eventType = ''; for (const line of lines) { @@ -420,7 +427,6 @@ export default function AgentChatPage(): React.ReactElement { setStreamingContent((prev) => prev + data.text); break; case 'tool_call': - // Add tool call to tracking setToolCalls((prev) => [ ...prev, { @@ -430,11 +436,9 @@ export default function AgentChatPage(): React.ReactElement { status: 'running', }, ]); - // Auto-expand new tool calls setExpandedToolCalls((prev) => new Set([...prev, data.toolCallId])); break; case 'tool_result': - // Update tool call with result setToolCalls((prev) => prev.map((tc) => tc.toolCallId === data.toolCallId @@ -444,13 +448,10 @@ export default function AgentChatPage(): React.ReactElement { ); break; case 'complete': - // Refresh messages - console.log('[SSE] Complete event received, fetching messages...'); await fetchMessages(); await fetchConversations(); setStreamingContent(''); setToolCalls([]); - console.log('[SSE] Messages and conversations refreshed'); break; case 'error': throw new Error(data.message); @@ -461,7 +462,6 @@ export default function AgentChatPage(): React.ReactElement { } catch (err) { console.error('Failed to send message:', err); setError(err instanceof Error ? err.message : 'Failed to send message'); - // Remove optimistic message on error setMessages((prev) => prev.filter((m) => m.id !== userMessage.id)); } finally { setIsSending(false); @@ -482,6 +482,15 @@ export default function AgentChatPage(): React.ReactElement { router.push(`/dashboard/agents/${agentId}/chat/${newId}`); }; + const handleSettingsChange = (newSettings: AgentSettings) => { + if (agent) { + setAgent({ + ...agent, + ...newSettings, + }); + } + }; + if (isLoading) { return ( + +
@@ -561,50 +593,57 @@ export default function AgentChatPage(): React.ReactElement { >
{/* Conversations Sidebar */} -
- {/* Conversations List */} +
+
+ + conversations + +
{conversations.length === 0 ? ( -

- No conversations yet +

+ no conversations yet

) : ( conversations.map((conv) => ( - +

{conv.title || 'Untitled Chat'}

+

+ {conv.messageCount} messages +

+ )) )}
{/* Chat Area */} -
+
{/* Messages */}
{messages.length === 0 && !streamingContent && (
-
-
+
+ + new conversation + +
-

Start a conversation

-

- Send a message to start chatting with {agent.name}. +

start chatting

+

+ Send a message to start a conversation with {agent.name}.

-
+
)} @@ -628,7 +667,6 @@ export default function AgentChatPage(): React.ReactElement { } return messages.map((message) => { - // Check if this ASSISTANT message has embedded tool calls const hasEmbeddedToolCalls = message.role === 'ASSISTANT' && message.toolCalls && @@ -641,13 +679,12 @@ export default function AgentChatPage(): React.ReactElement { {hasEmbeddedToolCalls && (
{message.toolCalls?.map((tc) => { - // Look up the output for this tool call const toolOutput = toolResultsMap.get(tc.toolCallId); const hasOutput = toolOutput !== undefined; return (
-
+
)} - {/* Render the message content - skip TOOL messages as they're shown with their calls */} + {/* Render the message content - skip TOOL messages */} {message.role !== 'TOOL' && (
{message.role === 'USER' ? ( @@ -698,7 +735,7 @@ export default function AgentChatPage(): React.ReactElement {
{toolCalls.map((tc) => (
-
+
-
+
{streamingContent}
@@ -723,10 +760,10 @@ export default function AgentChatPage(): React.ReactElement { {isSending && !streamingContent && toolCalls.length === 0 && (
-
+
- Thinking... + thinking...
@@ -737,13 +774,13 @@ export default function AgentChatPage(): React.ReactElement { {/* Error Message */} {error && ( -
-

{error}

+
+

{error}

)} {/* Input Area */} -
+