diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx
index 10646aa2..66abd0f2 100644
--- a/frontend/src/app/page.tsx
+++ b/frontend/src/app/page.tsx
@@ -1,222 +1,13 @@
'use client';
-import { useEffect } from 'react';
-import { useRouter } from 'next/navigation';
-import { useAuth } from '@/context/auth-context';
-import Link from 'next/link';
-import { Button } from '@/components/ui/button';
-import { Search, Image, Calculator } from 'lucide-react';
+import ComputerViewer from '@/components/ComputerViewer';
+
export default function HomePage() {
- const { user, isLoading } = useAuth();
- const router = useRouter();
-
- useEffect(() => {
- // Redirect authenticated users to dashboard
- if (!isLoading && user) {
- router.push('/dashboard');
- }
- }, [user, isLoading, router]);
-
- // If still loading or user is authenticated (redirecting), show loading
- if (isLoading || user) {
- return null;
- }
-
+
return (
- {/* Hero Section */}
-
-
-
AgentPress
-
- A meticulously AI-powered search engine with RAG and search grounding capabilities. Clean interface and built for everyone.
-
-
-
-
-
-
-
-
- {/* RAG & Search Grounding */}
-
-
-
RAG & Search Grounding
-
-
-
-
- 1
-
-
-
Enable modern RAG capabilities in your real-world applications
-
Semantic search provides better context for AI agents
-
-
-
-
-
- 2
-
-
-
Prioritized instant search
-
Get results as you type for a responsive experience
-
-
-
-
-
- 3
-
-
-
Integration is just a few lines of code for "information access"
-
Simple API to connect to your comprehensive search
-
-
-
-
-
-
-
- {/* Powered By */}
-
-
-
Powered By
-
-
-
-
-
-
Deployed on Vercel
-
-
-
-
-
-
Design by Tekky
-
-
-
-
-
- {/* Stats Section */}
-
-
-
-
350K+
-
Requests Processed
-
-
-
100K+
-
Active Users
-
-
-
7K+
-
Connections Built
-
-
-
-
- {/* Featured on Vercel's Blog */}
-
-
-
Featured on Vercel's Blog
-
-
-
- Recognized for our innovative use of AI technology and its integration for a seamless experience. Our approach to developer community growth stands out.
-
-
- Read the feature →
-
-
-
-
-
-
-
- {/* Advanced Features */}
-
-
-
Advanced Search Features
-
-
-
-
-
-
Smart Understanding
-
Easily analyze content and context for better search results
-
-
-
-
-
-
-
Image Understanding
-
Get contextual responses based on visual inputs
-
-
-
-
-
-
-
Smart Calculations
-
Perform complex math and calculations in real-time
-
-
-
-
-
- {/* Built For Everyone */}
-
-
-
Built For Everyone
-
-
-
Students
-
-
• Research paper analysis
-
• Complex calculations
-
• Study guidance
-
-
-
-
-
Researchers
-
-
• Access to paper archives
-
• Data visualization
-
• Citation formatting
-
-
-
-
-
Professionals
-
-
• Market research
-
• Technical troubleshooting
-
• Code reviews
-
-
-
-
-
-
- {/* Footer */}
-
+
);
}
diff --git a/frontendbasejump/package-lock.json b/frontendbasejump/package-lock.json
index d8d7436c..26ce267d 100644
--- a/frontendbasejump/package-lock.json
+++ b/frontendbasejump/package-lock.json
@@ -22,6 +22,8 @@
"clsx": "^2.1.0",
"cmdk": "^0.2.0",
"date-fns": "^3.6.0",
+ "diff": "^7.0.0",
+ "framer-motion": "^12.6.5",
"geist": "^1.2.1",
"lucide-react": "^0.368.0",
"next": "latest",
@@ -39,6 +41,7 @@
"zod": "^3.24.2"
},
"devDependencies": {
+ "@types/diff": "^7.0.2",
"@types/node": "20.11.5",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
@@ -1291,6 +1294,13 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/diff": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz",
+ "integrity": "sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/node": {
"version": "20.11.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz",
@@ -1992,6 +2002,15 @@
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"license": "Apache-2.0"
},
+ "node_modules/diff": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
+ "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
@@ -2113,6 +2132,33 @@
"url": "https://github.com/sponsors/rawify"
}
},
+ "node_modules/framer-motion": {
+ "version": "12.6.5",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.6.5.tgz",
+ "integrity": "sha512-MKvnWov0paNjvRJuIy6x418w23tFqRfS6CXHhZrCiSEpXVlo/F+usr8v4/3G6O0u7CpsaO1qop+v4Ip7PRCBqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.6.5",
+ "motion-utils": "^12.6.5",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2421,6 +2467,21 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/motion-dom": {
+ "version": "12.6.5",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.6.5.tgz",
+ "integrity": "sha512-jpM9TQLXzYMWMJ7Ec7sAj0iis8oIuu6WvjI3yNKJLdrZyrsI/b2cRInDVL8dCl683zQQq19DpL9cSMP+k8T1NA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.6.5"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.6.5",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.6.5.tgz",
+ "integrity": "sha512-IsOeKsOF+FWBhxQEDFBO6ZYC8/jlidmVbbLpe9/lXSA9j9kzGIMUuIBx2SZY+0reAS0DjZZ1i7dJp4NHrjocPw==",
+ "license": "MIT"
+ },
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
diff --git a/frontendbasejump/package.json b/frontendbasejump/package.json
index 6b27ff78..6cdfb1e1 100644
--- a/frontendbasejump/package.json
+++ b/frontendbasejump/package.json
@@ -23,6 +23,8 @@
"clsx": "^2.1.0",
"cmdk": "^0.2.0",
"date-fns": "^3.6.0",
+ "diff": "^7.0.0",
+ "framer-motion": "^12.6.5",
"geist": "^1.2.1",
"lucide-react": "^0.368.0",
"next": "latest",
@@ -40,6 +42,7 @@
"zod": "^3.24.2"
},
"devDependencies": {
+ "@types/diff": "^7.0.2",
"@types/node": "20.11.5",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
diff --git a/frontendbasejump/src/app/dashboard/(personalAccount)/agents/[threadId]/page.tsx b/frontendbasejump/src/app/dashboard/(personalAccount)/agents/[threadId]/page.tsx
index cc805b9f..62d5fd9a 100644
--- a/frontendbasejump/src/app/dashboard/(personalAccount)/agents/[threadId]/page.tsx
+++ b/frontendbasejump/src/app/dashboard/(personalAccount)/agents/[threadId]/page.tsx
@@ -1,13 +1,16 @@
'use client';
-import React, { useState, useEffect, useCallback, useRef } from "react";
-import { getProject, getMessages, getThread, addUserMessage, startAgent, stopAgent, getAgentRuns, createThread, type Message, type Project, type Thread, type AgentRun } from "@/lib/api";
+import React, { useState, useEffect, useCallback, useRef, useContext } from "react";
+import { getProject, getMessages, getThread, addUserMessage, startAgent, stopAgent, getAgentRuns, streamAgent, type Message, type Project, type Thread, type AgentRun } from "@/lib/api";
import { useRouter, useSearchParams } from "next/navigation";
-import { AlertCircle, ArrowDown, Play, Square } from "lucide-react";
+import { AlertCircle, Play, Square, Send, User, Plus } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Skeleton } from "@/components/ui/skeleton";
+import { SUPPORTED_XML_TAGS, ParsedTag } from "@/lib/types/tool-calls";
+import { ToolCallsContext } from "@/app/providers";
+import { getComponentForTag } from "@/components/tools/tool-components";
interface AgentPageProps {
params: {
@@ -15,16 +18,203 @@ interface AgentPageProps {
};
}
-// Simple component to handle message formatting
+// Parse XML tags in content
+function parseXMLTags(content: string): { parts: (string | ParsedTag)[], openTags: Record } {
+ const parts: (string | ParsedTag)[] = [];
+ const openTags: Record = {};
+ const tagStack: Array<{tagName: string, position: number}> = [];
+
+ // Find all opening and closing tags
+ let currentPosition = 0;
+
+ // Match opening tags with attributes like
+ const openingTagRegex = new RegExp(`<(${SUPPORTED_XML_TAGS.join('|')})\\s*([^>]*)>`, 'g');
+ // Match closing tags like
+ const closingTagRegex = new RegExp(`(${SUPPORTED_XML_TAGS.join('|')})>`, 'g');
+
+ let match: RegExpExecArray | null;
+ let matches: { regex: RegExp, match: RegExpExecArray, isOpening: boolean, position: number }[] = [];
+
+ // Find all opening tags
+ while ((match = openingTagRegex.exec(content)) !== null) {
+ matches.push({
+ regex: openingTagRegex,
+ match,
+ isOpening: true,
+ position: match.index
+ });
+ }
+
+ // Find all closing tags
+ while ((match = closingTagRegex.exec(content)) !== null) {
+ matches.push({
+ regex: closingTagRegex,
+ match,
+ isOpening: false,
+ position: match.index
+ });
+ }
+
+ // Sort matches by their position in the content
+ matches.sort((a, b) => a.position - b.position);
+
+ // Process matches in order
+ for (const { match, isOpening, position } of matches) {
+ const tagName = match[1];
+ const matchEnd = position + match[0].length;
+
+ // Add text before this tag if needed
+ if (position > currentPosition) {
+ parts.push(content.substring(currentPosition, position));
+ }
+
+ if (isOpening) {
+ // Parse attributes for opening tags
+ const attributesStr = match[2]?.trim();
+ const attributes: Record = {};
+
+ if (attributesStr) {
+ // Match attributes in format: name="value" or name='value'
+ const attrRegex = /(\w+)=["']([^"']*)["']/g;
+ let attrMatch;
+ while ((attrMatch = attrRegex.exec(attributesStr)) !== null) {
+ attributes[attrMatch[1]] = attrMatch[2];
+ }
+ }
+
+ // Create tag object with unique ID
+ const parsedTag: ParsedTag = {
+ tagName,
+ attributes,
+ content: '',
+ isClosing: false,
+ id: `${tagName}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
+ rawMatch: match[0]
+ };
+
+ // Add timestamp if not present
+ if (!parsedTag.timestamp) {
+ parsedTag.timestamp = Date.now();
+ }
+
+ // Push to parts and track in stack
+ parts.push(parsedTag);
+ tagStack.push({ tagName, position: parts.length - 1 });
+ openTags[tagName] = parsedTag;
+
+ } else {
+ // Handle closing tag
+ // Find the corresponding opening tag in the stack (last in, first out)
+ let foundOpeningTag = false;
+
+ for (let i = tagStack.length - 1; i >= 0; i--) {
+ if (tagStack[i].tagName === tagName) {
+ const openTagIndex = tagStack[i].position;
+ const openTag = parts[openTagIndex] as ParsedTag;
+
+ // Get content between this opening and closing tag pair
+ const contentStart = position;
+ let tagContentStart = openTagIndex + 1;
+ let tagContentEnd = parts.length;
+
+ // Mark that we need to capture content between these positions
+ let contentToCapture = '';
+
+ // Collect all content parts between the opening and closing tags
+ for (let j = tagContentStart; j < tagContentEnd; j++) {
+ if (typeof parts[j] === 'string') {
+ contentToCapture += parts[j];
+ }
+ }
+
+ // Try getting content directly from original text (most reliable approach)
+ const openTagMatch = openTag.rawMatch || '';
+ const openTagPosition = content.indexOf(openTagMatch, Math.max(0, openTagIndex > 0 ? currentPosition - 200 : 0));
+ if (openTagPosition >= 0) {
+ const openTagEndPosition = openTagPosition + openTagMatch.length;
+ // Only use if the positions make sense
+ if (openTagEndPosition > 0 && position > openTagEndPosition) {
+ // Get content and clean up excessive whitespace but preserve formatting
+ let extractedContent = content.substring(openTagEndPosition, position);
+
+ // Trim leading newline if present
+ if (extractedContent.startsWith('\n')) {
+ extractedContent = extractedContent.substring(1);
+ }
+
+ // Trim trailing newline if present
+ if (extractedContent.endsWith('\n')) {
+ extractedContent = extractedContent.substring(0, extractedContent.length - 1);
+ }
+
+ contentToCapture = extractedContent;
+
+ // Debug info in development
+ console.log(`[XML Parse] Extracted content for ${tagName}:`, contentToCapture);
+ }
+ }
+
+ // Update opening tag with collected content
+ openTag.content = contentToCapture;
+ openTag.isClosing = true;
+
+ // Remove all parts between the opening tag and this position
+ // because they're now captured in the tag's content
+ if (tagContentStart < tagContentEnd) {
+ parts.splice(tagContentStart, tagContentEnd - tagContentStart);
+ }
+
+ // Remove this tag from the stack
+ tagStack.splice(i, 1);
+ // Remove from openTags
+ delete openTags[tagName];
+
+ foundOpeningTag = true;
+ break;
+ }
+ }
+
+ // If no corresponding opening tag found, add closing tag as text
+ if (!foundOpeningTag) {
+ parts.push(match[0]);
+ }
+ }
+
+ currentPosition = matchEnd;
+ }
+
+ // Add any remaining text
+ if (currentPosition < content.length) {
+ parts.push(content.substring(currentPosition));
+ }
+
+ return { parts, openTags };
+}
+
+// Simple component to handle message formatting with XML tag support
function MessageContent({ content }: { content: string }) {
+ const { parts, openTags } = parseXMLTags(content);
+
return (
+ );
+ })}
+
+ {/* Show streaming content if available */}
+ {streamContent && (
+
+
+
+
+ {isStreaming && (
+
+ )}
+
+
- ))}
+ )}
- {/* Show a loading indicator if the agent is running */}
- {isAgentRunning && (
+ {/* Show a loading indicator if the agent is running but no stream yet */}
+ {isAgentRunning && !streamContent && (
@@ -500,7 +528,7 @@ export default function DashboardLayout({
{/* Floating draggable right panel - only show when right sidebar is collapsed and visible */}
- {showRightSidebar && rightSidebarCollapsed && rightPanelContent && (
+ {showRightSidebar && rightSidebarCollapsed && effectiveRightPanelContent && (
- {rightPanelTitle}
+ {effectiveRightPanelTitle}
@@ -566,7 +594,7 @@ export default function DashboardLayout({
{/* Right Sidebar - only show when visible */}
- {showRightSidebar && rightPanelContent && (
+ {showRightSidebar && effectiveRightPanelContent && (