diff --git a/INSTALL.md b/INSTALL.md
index d46f787af..6c0a8a276 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -176,30 +176,6 @@ CREATE TABLE users (
CONSTRAINT users_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE -- Explicit FK definition
);
--- Agents table
-CREATE TABLE agents (
- id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
- name TEXT NOT NULL,
- slug TEXT NOT NULL,
- description TEXT NOT NULL,
- avatar_url TEXT,
- system_prompt TEXT NOT NULL,
- model_preference TEXT,
- is_public BOOLEAN DEFAULT false NOT NULL,
- remixable BOOLEAN DEFAULT false NOT NULL,
- tools_enabled BOOLEAN DEFAULT false NOT NULL,
- example_inputs TEXT[],
- tags TEXT[],
- category TEXT,
- creator_id UUID,
- created_at TIMESTAMPTZ DEFAULT NOW(),
- updated_at TIMESTAMPTZ,
- tools TEXT[],
- max_steps INTEGER,
- mcp_config JSONB, -- Representing the object structure as JSONB
- CONSTRAINT agents_creator_id_fkey FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL -- Changed to SET NULL based on schema, could also be CASCADE
-);
-
-- Projects table
CREATE TABLE projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -217,12 +193,10 @@ CREATE TABLE chats (
title TEXT,
model TEXT,
system_prompt TEXT,
- agent_id UUID,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
public BOOLEAN DEFAULT FALSE NOT NULL,
CONSTRAINT chats_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
- CONSTRAINT chats_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
CONSTRAINT chats_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
);
@@ -281,7 +255,7 @@ CREATE TABLE user_keys (
-- ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- CREATE POLICY "Users can view their own data." ON users FOR SELECT USING (auth.uid() = id);
-- CREATE POLICY "Users can update their own data." ON users FOR UPDATE USING (auth.uid() = id);
--- ... add policies for other tables (agents, chats, messages, etc.) ...
+-- ... add policies for other tables (chats, messages, etc.) ...
```
### Storage Setup
@@ -292,25 +266,6 @@ Create the buckets `chat-attachments` and `avatars` in your Supabase dashboard:
2. Click "New bucket" and create two buckets: `chat-attachments` and `avatars`
3. Configure public access permissions for both buckets
-#### Agent Avatar Configuration
-
-For agent profile pictures to work properly:
-
-1. Create an `agents` folder inside your `avatars` bucket:
-
- - Navigate to the `avatars` bucket
- - Click "Create folder" and name it `agents`
-
-2. Upload agent avatar images
-
-3. Set up public access for the avatars bucket:
- - Go to "Configuration" tab for the `avatars` bucket
- - Under "Row Level Security (RLS)" ensure it's disabled or create a policy:
- ```sql
- CREATE POLICY "Public Read Access" ON storage.objects
- FOR SELECT USING (bucket_id = 'avatars');
- ```
-
## Ollama Setup (Local AI Models)
Ollama allows you to run AI models locally on your machine. Zola has built-in support for Ollama with automatic model detection.
diff --git a/README.md b/README.md
index 1b737b74f..c3dd8d6ef 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,6 @@
- Open-source and self-hostable
- Customizable: user system prompt, multiple layout options
- Local AI with Ollama: Run models locally with automatic model detection
-- Basic agent (wip)
- Full MCP support (wip)
## Quick Start
@@ -57,7 +56,7 @@ docker-compose -f docker-compose.ollama.yml up
[](https://vercel.com/new/clone?repository-url=https://github.com/ibelick/zola)
-To unlock features like auth, file uploads, and agents, see [INSTALL.md](./INSTALL.md).
+To unlock features like auth, file uploads, see [INSTALL.md](./INSTALL.md).
## Built with
diff --git a/app/agents/[...agentSlug]/page.tsx b/app/agents/[...agentSlug]/page.tsx
deleted file mode 100644
index cbd276e4e..000000000
--- a/app/agents/[...agentSlug]/page.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { AgentDetail } from "@/app/components/agents/agent-detail"
-import { LayoutApp } from "@/app/components/layout/layout-app"
-import { MessagesProvider } from "@/lib/chat-store/messages/provider"
-import { isSupabaseEnabled } from "@/lib/supabase/config"
-import { createClient } from "@/lib/supabase/server"
-import { notFound } from "next/navigation"
-
-export default async function AgentIdPage({
- params,
-}: {
- params: Promise<{ agentSlug: string | string[] }>
-}) {
- if (!isSupabaseEnabled) {
- notFound()
- }
-
- const { agentSlug: slugParts } = await params
- const agentSlug = Array.isArray(slugParts) ? slugParts.join("/") : slugParts
-
- const supabase = await createClient()
-
- if (!supabase) {
- notFound()
- }
-
- const { data: agent, error: agentError } = await supabase
- .from("agents")
- .select("*")
- .eq("slug", agentSlug)
- .single()
-
- if (agentError) {
- throw new Error(agentError.message)
- }
-
- const { data: agents, error: agentsError } = await supabase
- .from("agents")
- .select("*")
- .not("slug", "eq", agentSlug)
- .limit(4)
-
- if (agentsError) {
- throw new Error(agentsError.message)
- }
-
- return (
-
-
-
-
-
- )
-}
diff --git a/app/agents/page.tsx b/app/agents/page.tsx
deleted file mode 100644
index d1a990cb7..000000000
--- a/app/agents/page.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { AgentsPage } from "@/app/components/agents/agents-page"
-import { LayoutApp } from "@/app/components/layout/layout-app"
-import { MessagesProvider } from "@/lib/chat-store/messages/provider"
-import { CURATED_AGENTS_SLUGS } from "@/lib/config"
-import { isSupabaseEnabled } from "@/lib/supabase/config"
-import { createClient } from "@/lib/supabase/server"
-import { notFound } from "next/navigation"
-
-export const dynamic = "force-dynamic"
-
-export default async function Page() {
- if (!isSupabaseEnabled) {
- notFound()
- }
-
- const supabase = await createClient()
-
- if (!supabase) {
- notFound()
- }
-
- const { data: userData } = await supabase.auth.getUser()
-
- const { data: curatedAgents, error: agentsError } = await supabase
- .from("agents")
- .select("*")
- .in("slug", CURATED_AGENTS_SLUGS)
-
- const { data: userAgents, error: userAgentsError } = userData?.user?.id
- ? await supabase
- .from("agents")
- .select("*")
- .eq("creator_id", userData?.user?.id)
- : { data: [], error: null }
-
- if (agentsError) {
- console.error(agentsError)
- return
Error loading agents
- }
-
- if (userAgentsError) {
- console.error(userAgentsError)
- return Error loading user agents
- }
-
- return (
-
-
-
-
-
- )
-}
diff --git a/app/api/chat/api.ts b/app/api/chat/api.ts
index b6bfbc8c4..c84757056 100644
--- a/app/api/chat/api.ts
+++ b/app/api/chat/api.ts
@@ -1,14 +1,13 @@
import { saveFinalAssistantMessage } from "@/app/api/chat/db"
-import { checkSpecialAgentUsage, incrementSpecialAgentUsage } from "@/lib/api"
+import type {
+ ChatApiParams,
+ LogUserMessageParams,
+ StoreAssistantMessageParams,
+ SupabaseClientType,
+} from "@/app/types/api.types"
import { sanitizeUserInput } from "@/lib/sanitize"
import { validateUserIdentity } from "@/lib/server/api"
import { checkUsageByModel, incrementUsageByModel } from "@/lib/usage"
-import type {
- SupabaseClientType,
- ChatApiParams,
- LogUserMessageParams,
- StoreAssistantMessageParams
-} from "@/app/types/api.types"
export async function validateAndTrackUsage({
userId,
@@ -48,12 +47,6 @@ export async function logUserMessage({
}
}
-export async function trackSpecialAgentUsage(supabase: SupabaseClientType, userId: string): Promise {
- if (!supabase) return
- await checkSpecialAgentUsage(supabase, userId)
- await incrementSpecialAgentUsage(supabase, userId)
-}
-
export async function storeAssistantMessage({
supabase,
chatId,
diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts
index 4732c4049..cf06d917e 100644
--- a/app/api/chat/route.ts
+++ b/app/api/chat/route.ts
@@ -1,6 +1,4 @@
-import { loadAgent } from "@/lib/agents/load-agent"
import { SYSTEM_PROMPT_DEFAULT } from "@/lib/config"
-import { loadMCPToolsFromURL } from "@/lib/mcp/load-mcp-from-url"
import { getAllModels } from "@/lib/models"
import { getProviderForModel } from "@/lib/openproviders/provider-map"
import type { ProviderWithoutOllama } from "@/lib/user-keys"
@@ -9,14 +7,9 @@ import { Message as MessageAISDK, streamText, ToolSet } from "ai"
import {
logUserMessage,
storeAssistantMessage,
- trackSpecialAgentUsage,
validateAndTrackUsage,
} from "./api"
-import {
- cleanMessagesForTools,
- createErrorResponse,
- extractErrorMessage,
-} from "./utils"
+import { createErrorResponse, extractErrorMessage } from "./utils"
export const maxDuration = 60
@@ -27,7 +20,6 @@ type ChatRequest = {
model: string
isAuthenticated: boolean
systemPrompt: string
- agentId: string | null
enableSearch: boolean
}
@@ -40,7 +32,6 @@ export async function POST(req: Request) {
model,
isAuthenticated,
systemPrompt,
- agentId,
enableSearch,
} = (await req.json()) as ChatRequest
@@ -71,12 +62,6 @@ export async function POST(req: Request) {
})
}
- let agentConfig = null
-
- if (agentId) {
- agentConfig = await loadAgent(agentId)
- }
-
const allModels = await getAllModels()
const modelConfig = allModels.find((m) => m.id === model)
@@ -84,24 +69,7 @@ export async function POST(req: Request) {
throw new Error(`Model ${model} not found`)
}
- const effectiveSystemPrompt =
- agentConfig?.systemPrompt || systemPrompt || SYSTEM_PROMPT_DEFAULT
-
- let toolsToUse = undefined
-
- if (agentConfig?.mcpConfig) {
- const { tools } = await loadMCPToolsFromURL(agentConfig.mcpConfig.server)
- toolsToUse = tools
- } else if (agentConfig?.tools) {
- toolsToUse = agentConfig.tools
- if (supabase) {
- await trackSpecialAgentUsage(supabase, userId)
- }
- }
-
- // Clean messages when switching between agents with different tool capabilities
- const hasTools = !!toolsToUse && Object.keys(toolsToUse).length > 0
- const cleanedMessages = cleanMessagesForTools(messages, hasTools)
+ const effectiveSystemPrompt = systemPrompt || SYSTEM_PROMPT_DEFAULT
let apiKey: string | undefined
if (isAuthenticated && userId) {
@@ -115,8 +83,8 @@ export async function POST(req: Request) {
const result = streamText({
model: modelConfig.apiSdk(apiKey, { enableSearch }),
system: effectiveSystemPrompt,
- messages: cleanedMessages,
- tools: toolsToUse as ToolSet,
+ messages: messages,
+ tools: {} as ToolSet,
maxSteps: 10,
onError: (err: unknown) => {
console.error("Streaming error occurred:", err)
diff --git a/app/api/create-agent/route.ts b/app/api/create-agent/route.ts
deleted file mode 100644
index b474e4530..000000000
--- a/app/api/create-agent/route.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { createClient } from "@/lib/supabase/server"
-import { nanoid } from "nanoid"
-import slugify from "slugify"
-
-function generateAgentSlug(title: string) {
- const base = slugify(title, { lower: true, strict: true, trim: true })
- const id = nanoid(6)
- return `${base}-${id}`
-}
-
-export async function POST(request: Request) {
- try {
- const {
- name,
- description,
- systemPrompt,
- mcp_config,
- example_inputs,
- avatar_url,
- tools = [],
- remixable = false,
- is_public = true,
- max_steps = 5,
- } = await request.json()
-
- if (!name || !description || !systemPrompt) {
- return new Response(
- JSON.stringify({ error: "Missing required fields" }),
- {
- status: 400,
- }
- )
- }
-
- const supabase = await createClient()
-
- if (!supabase) {
- return new Response(
- JSON.stringify({ error: "Supabase not available in this deployment." }),
- { status: 200 }
- )
- }
-
- const { data: authData } = await supabase.auth.getUser()
-
- if (!authData?.user?.id) {
- return new Response(JSON.stringify({ error: "Missing userId" }), {
- status: 400,
- })
- }
-
- const { data: agent, error: supabaseError } = await supabase
- .from("agents")
- .insert({
- slug: generateAgentSlug(name),
- name,
- description,
- avatar_url,
- mcp_config,
- example_inputs,
- tools,
- remixable,
- is_public,
- system_prompt: systemPrompt,
- max_steps,
- creator_id: authData.user.id,
- })
- .select()
- .single()
-
- if (supabaseError) {
- return new Response(JSON.stringify({ error: supabaseError.message }), {
- status: 500,
- })
- }
-
- return new Response(JSON.stringify({ agent }), { status: 201 })
- } catch (err: unknown) {
- console.error("Error in create-agent endpoint:", err)
-
- return new Response(
- JSON.stringify({ error: (err as Error).message || "Internal server error" }),
- { status: 500 }
- )
- }
-}
diff --git a/app/api/create-chat/api.ts b/app/api/create-chat/api.ts
index 5de3cb349..6e7e9db81 100644
--- a/app/api/create-chat/api.ts
+++ b/app/api/create-chat/api.ts
@@ -1,4 +1,3 @@
-import { filterLocalAgentId } from "@/lib/agents/utils"
import { validateUserIdentity } from "@/lib/server/api"
import { checkUsageByModel } from "@/lib/usage"
@@ -7,7 +6,6 @@ type CreateChatInput = {
title?: string
model: string
isAuthenticated: boolean
- agentId?: string
projectId?: string
}
@@ -16,12 +14,8 @@ export async function createChatInDb({
title,
model,
isAuthenticated,
- agentId,
projectId,
}: CreateChatInput) {
- // Filter out local agent IDs for database operations
- const dbAgentId = filterLocalAgentId(agentId)
-
const supabase = await validateUserIdentity(userId, isAuthenticated)
if (!supabase) {
return {
@@ -31,7 +25,6 @@ export async function createChatInDb({
model,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
- agent_id: dbAgentId,
}
}
@@ -41,7 +34,6 @@ export async function createChatInDb({
user_id: string
title: string
model: string
- agent_id?: string
project_id?: string
} = {
user_id: userId,
@@ -49,10 +41,6 @@ export async function createChatInDb({
model,
}
- if (dbAgentId) {
- insertData.agent_id = dbAgentId
- }
-
if (projectId) {
insertData.project_id = projectId
}
diff --git a/app/api/create-chat/route.ts b/app/api/create-chat/route.ts
index 45ff91df8..ca68d869e 100644
--- a/app/api/create-chat/route.ts
+++ b/app/api/create-chat/route.ts
@@ -2,7 +2,7 @@ import { createChatInDb } from "./api"
export async function POST(request: Request) {
try {
- const { userId, title, model, isAuthenticated, agentId, projectId } =
+ const { userId, title, model, isAuthenticated, projectId } =
await request.json()
if (!userId) {
@@ -16,7 +16,6 @@ export async function POST(request: Request) {
title,
model,
isAuthenticated,
- agentId,
projectId,
})
diff --git a/app/api/delete-agent/route.ts b/app/api/delete-agent/route.ts
deleted file mode 100644
index 7e98a6a4a..000000000
--- a/app/api/delete-agent/route.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { createClient } from "@/lib/supabase/server"
-
-export async function DELETE(request: Request) {
- try {
- const { slug } = await request.json()
-
- if (!slug) {
- return new Response(JSON.stringify({ error: "Missing agent slug" }), {
- status: 400,
- })
- }
-
- const supabase = await createClient()
-
- if (!supabase) {
- return new Response(
- JSON.stringify({ error: "Supabase not available in this deployment." }),
- { status: 500 }
- )
- }
-
- // Get the authenticated user
- const { data: authData, error: authError } = await supabase.auth.getUser()
-
- if (authError || !authData?.user?.id) {
- return new Response(
- JSON.stringify({ error: "Authentication required" }),
- { status: 401 }
- )
- }
-
- // First, check if the agent exists and the user owns it
- const { data: agent, error: fetchError } = await supabase
- .from("agents")
- .select("id, creator_id, name")
- .eq("slug", slug)
- .single()
-
- if (fetchError || !agent) {
- return new Response(JSON.stringify({ error: "Agent not found" }), {
- status: 404,
- })
- }
-
- if (agent.creator_id !== authData.user.id) {
- return new Response(
- JSON.stringify({
- error: "You can only delete agents that you created",
- }),
- { status: 403 }
- )
- }
-
- // Delete the agent
- const { error: deleteError } = await supabase
- .from("agents")
- .delete()
- .eq("slug", slug)
- .eq("creator_id", authData.user.id) // Extra safety check
-
- if (deleteError) {
- console.error("Error deleting agent:", deleteError)
- return new Response(
- JSON.stringify({
- error: "Failed to delete agent",
- details: deleteError.message,
- }),
- { status: 500 }
- )
- }
-
- return new Response(
- JSON.stringify({ message: "Agent deleted successfully" }),
- { status: 200 }
- )
- } catch (err: unknown) {
- console.error("Error in delete-agent endpoint:", err)
-
- return new Response(
- JSON.stringify({ error: (err as Error).message || "Internal server error" }),
- { status: 500 }
- )
- }
-}
diff --git a/app/api/developer-tools/route.ts b/app/api/developer-tools/route.ts
deleted file mode 100644
index 389306b15..000000000
--- a/app/api/developer-tools/route.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { NextResponse } from "next/server"
-
-export async function GET() {
- // Only allow in development
- if (process.env.NODE_ENV !== "development") {
- return NextResponse.json(
- { error: "Not available in production" },
- { status: 403 }
- )
- }
-
- const getMaskedKey = (key: string | undefined) => {
- if (!key || key.length < 4) return null
- return `${"*".repeat(8)}${key.slice(-3)}`
- }
-
- const tools = [
- {
- id: "exa",
- name: "Exa",
- icon: "đ§ ",
- description: "Use Exa to power search-based agents.",
- envKeys: ["EXA_API_KEY"],
- connected: Boolean(process.env.EXA_API_KEY),
- maskedKey: getMaskedKey(process.env.EXA_API_KEY),
- sampleEnv: `EXA_API_KEY=your_key_here`,
- },
- {
- id: "github",
- name: "GitHub",
- icon: "đ",
- description: "Use GitHub Search in your agents.",
- envKeys: ["GITHUB_TOKEN"],
- connected: Boolean(process.env.GITHUB_TOKEN),
- maskedKey: getMaskedKey(process.env.GITHUB_TOKEN),
- sampleEnv: `GITHUB_TOKEN=your_token_here`,
- },
- ]
-
- return NextResponse.json({ tools })
-}
diff --git a/app/api/tools-available/route.ts b/app/api/tools-available/route.ts
deleted file mode 100644
index 022a3ff6f..000000000
--- a/app/api/tools-available/route.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { TOOL_REGISTRY } from "@/lib/tools"
-import { NextResponse } from "next/server"
-
-export async function GET() {
- const availableToolIds = Object.entries(TOOL_REGISTRY)
- .filter(([, tool]) => tool?.isAvailable?.())
- .map(([id]) => id)
-
- return NextResponse.json({ available: availableToolIds })
-}
diff --git a/app/api/update-chat-agent/route.ts b/app/api/update-chat-agent/route.ts
deleted file mode 100644
index 50ba74813..000000000
--- a/app/api/update-chat-agent/route.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { filterLocalAgentId } from "@/lib/agents/utils"
-import { validateUserIdentity } from "@/lib/server/api"
-
-export async function POST(request: Request) {
- try {
- const { userId, chatId, agentId, isAuthenticated } = await request.json()
-
- if (!userId || !chatId) {
- return new Response(
- JSON.stringify({ error: "Missing userId or chatId" }),
- { status: 400 }
- )
- }
-
- // Filter out local agent IDs for database operations
- const dbAgentId = filterLocalAgentId(agentId)
-
- const supabase = await validateUserIdentity(userId, isAuthenticated)
-
- if (!supabase) {
- console.log("Supabase not enabled, skipping agent update")
- return new Response(
- JSON.stringify({ chat: { id: chatId, agent_id: dbAgentId } }),
- {
- status: 200,
- }
- )
- }
-
- const { data, error: updateError } = await supabase
- .from("chats")
- .update({ agent_id: dbAgentId || null })
- .eq("id", chatId)
- .select()
- .single()
-
- if (updateError) {
- return new Response(JSON.stringify({ error: "Failed to update chat" }), {
- status: 500,
- })
- }
-
- return new Response(JSON.stringify({ chat: data }), {
- status: 200,
- })
- } catch (error) {
- console.error("Error updating chat agent:", error)
- return new Response(
- JSON.stringify({ error: "Failed to update chat agent" }),
- { status: 500 }
- )
- }
-}
diff --git a/app/components/agents/agent-card.tsx b/app/components/agents/agent-card.tsx
deleted file mode 100644
index 6fe149964..000000000
--- a/app/components/agents/agent-card.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import { Tables } from "@/app/types/database.types"
-import { Avatar, AvatarImage } from "@/components/ui/avatar"
-import { cn } from "@/lib/utils"
-
-type AgentCardProps = {
- id: string
- name: string
- description: string
- avatar_url?: string | null
- className?: string
- isAvailable: boolean
- onClick?: () => void
- system_prompt?: string
- tools?: string[] | null
- mcp_config?: Tables<"agents">["mcp_config"] | null
- isLight?: boolean
-}
-
-export function AgentCard({
- name,
- description,
- avatar_url,
- className,
- isAvailable,
- onClick,
- system_prompt,
- tools,
- mcp_config,
- isLight = false,
-}: AgentCardProps) {
- return (
-
- )
-}
diff --git a/app/components/agents/agent-detail.tsx b/app/components/agents/agent-detail.tsx
deleted file mode 100644
index 203092f1f..000000000
--- a/app/components/agents/agent-detail.tsx
+++ /dev/null
@@ -1,442 +0,0 @@
-"use client"
-
-import { AgentSummary } from "@/app/types/agent"
-import type { Tables } from "@/app/types/database.types"
-import { ButtonCopy } from "@/components/common/button-copy"
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
-} from "@/components/ui/alert-dialog"
-import { Avatar, AvatarImage } from "@/components/ui/avatar"
-import { Button } from "@/components/ui/button"
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
-import { toast } from "@/components/ui/toast"
-import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from "@/components/ui/tooltip"
-import { fetchClient } from "@/lib/fetch"
-import { API_ROUTE_DELETE_AGENT } from "@/lib/routes"
-import { useUser } from "@/lib/user-store/provider"
-import { cn } from "@/lib/utils"
-import {
- ChatCircle,
- Check,
- CopySimple,
- Cube,
- DotsThree,
- Trash,
- X,
-} from "@phosphor-icons/react"
-import { useRouter } from "next/navigation"
-import { useRef, useState } from "react"
-
-function SystemPromptDisplay({ prompt }: { prompt: string }) {
- const [expanded, setExpanded] = useState(false)
- const isLong = prompt.length > 300
-
- const displayText =
- isLong && !expanded ? prompt.slice(0, 300) + "..." : prompt
-
- return (
-
-
-
-
-
- {displayText}
-
- {isLong && (
-
- )}
-
- )
-}
-
-type AgentDetailProps = {
- slug: string
- name: string
- description: string
- example_inputs: string[]
- creator_id?: string | null
- avatar_url?: string | null
- onAgentClick?: (agentId: string) => void
- randomAgents: AgentSummary[]
- isFullPage?: boolean
- system_prompt?: string | null
- tools?: string[] | null
- mcp_config?: Tables<"agents">["mcp_config"] | null
-}
-
-export function AgentDetail({
- slug,
- name,
- description,
- example_inputs,
- creator_id,
- avatar_url,
- onAgentClick,
- randomAgents,
- isFullPage,
- system_prompt,
- tools,
- mcp_config,
-}: AgentDetailProps) {
- const [copied, setCopied] = useState(false)
- const [showDeleteDialog, setShowDeleteDialog] = useState(false)
- const [isDeleting, setIsDeleting] = useState(false)
- const router = useRouter()
- const { user } = useUser()
- const didPrefetch = useRef(false)
-
- if (!didPrefetch.current && isFullPage && randomAgents.length > 0) {
- randomAgents.forEach((agent) => {
- router.prefetch(`/agents/${agent.slug}`)
- })
- didPrefetch.current = true
- }
-
- const copyToClipboard = () => {
- navigator.clipboard.writeText(`${window.location.origin}/agents/${slug}`)
- setCopied(true)
- setTimeout(() => setCopied(false), 1000)
- }
-
- const handleAgentClick = (agent: AgentSummary) => {
- if (onAgentClick) {
- onAgentClick(agent.id)
- } else {
- router.push(`/agents/${agent.slug}`)
- }
- }
-
- const tryAgentWithPrompt = async (prompt: string) => {
- router.push(`/?agent=${slug}&prompt=${encodeURIComponent(prompt)}`)
- }
-
- const tryAgent = async () => {
- router.push(`/?agent=${slug}`)
- }
-
- const handleDelete = async () => {
- if (!user?.id) {
- toast({
- title: "Error",
- description: "You must be logged in to delete an agent.",
- status: "error",
- })
- return
- }
-
- if (creator_id !== user.id) {
- toast({
- title: "Error",
- description: "You can only delete agents that you created.",
- status: "error",
- })
- return
- }
-
- setIsDeleting(true)
- try {
- const response = await fetchClient(API_ROUTE_DELETE_AGENT, {
- method: "DELETE",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ slug }),
- })
-
- const data = await response.json()
-
- if (!response.ok) {
- throw new Error(data.error || `HTTP ${response.status}`)
- }
-
- toast({
- title: "Success",
- description: "Agent deleted successfully.",
- status: "success",
- })
-
- setShowDeleteDialog(false)
-
- // If we're in a dialog (not full page), close it first
- if (!isFullPage && onAgentClick) {
- onAgentClick("")
- }
-
- // Navigate to agents page
- router.push("/agents")
- } catch (error) {
- console.error("Failed to delete agent:", error)
- toast({
- title: "Error",
- description:
- error instanceof Error
- ? error.message
- : "Failed to delete agent. Please try again.",
- status: "error",
- })
- } finally {
- setIsDeleting(false)
- }
- }
-
- const canDelete = user?.id && creator_id === user.id
-
- return (
-
-
-
-
- {avatar_url ? (
-
-
-
- ) : (
-
-
-
- )}
-
{name}
-
-
-
- {isFullPage && canDelete && (
-
-
-
-
-
- setShowDeleteDialog(true)}
- >
-
- Delete agent
-
-
-
- )}
- {!isFullPage && (
-
- )}
-
-
-
-
-
- {system_prompt && (
-
-
System Prompt
-
-
- )}
-
- {(tools || mcp_config) && (
-
- {tools && (
-
- )}
- {mcp_config && (
-
-
MCP
-
- {JSON.stringify(mcp_config)}
-
-
- )}
-
- )}
-
- {example_inputs && example_inputs.length > 0 && (
-
-
What can I ask?
-
- {example_inputs.map((example_input) => (
-
- ))}
-
-
- )}
-
- {randomAgents && randomAgents.length > 0 && (
-
-
- More agents
-
-
- {randomAgents.map((agent, index) => (
-
handleAgentClick(agent)}
- className={cn(
- "bg-secondary hover:bg-accent h-full cursor-pointer rounded-xl p-4 transition-colors",
- isFullPage ? "w-full" : "min-w-[280px]",
- index === randomAgents.length - 1 && "mr-6"
- )}
- >
-
-
- {agent.avatar_url ? (
-
- ) : (
-
-
-
- )}
-
-
-
- {agent.name}
-
-
- {agent.description}
-
-
-
-
- ))}
-
-
- )}
-
-
-
-
-
-
-
-
- {copied ? "Copied to clipboard" : "Copy link to clipboard"}
-
-
-
-
-
- {/* Only show AlertDialog in full page mode to avoid nesting issues */}
- {isFullPage && (
-
-
-
- Delete Agent
-
- Are you sure you want to delete "{name}"? This action
- cannot be undone.
-
-
-
- Cancel
-
- {isDeleting ? "Deleting..." : "Delete"}
-
-
-
-
- )}
-
- )
-}
diff --git a/app/components/agents/agent-featured-section.tsx b/app/components/agents/agent-featured-section.tsx
deleted file mode 100644
index e0ca938cf..000000000
--- a/app/components/agents/agent-featured-section.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Agent } from "@/app/types/agent"
-import React from "react"
-import { DialogAgent } from "./dialog-agent"
-
-type AgentFeaturedSectionProps = {
- agents: Agent[]
- handleAgentClick: (agentId: string | null) => void
- openAgentId: string | null
- setOpenAgentId: (agentId: string | null) => void
- moreAgents: Agent[]
-}
-
-export function AgentFeaturedSection({
- agents,
- moreAgents,
- handleAgentClick,
- openAgentId,
- setOpenAgentId,
-}: AgentFeaturedSectionProps) {
- if (!agents || agents.length === 0) {
- return null
- }
-
- return (
-
-
Featured
-
- {agents.map((agent) => (
- setOpenAgentId(open ? agent.id : null)}
- randomAgents={moreAgents}
- />
- ))}
-
-
- )
-}
diff --git a/app/components/agents/agents-page.tsx b/app/components/agents/agents-page.tsx
deleted file mode 100644
index 1fb5153de..000000000
--- a/app/components/agents/agents-page.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-"use client"
-
-import { Agent } from "@/app/types/agent"
-import { Button } from "@/components/ui/button"
-import { useMemo, useState } from "react"
-import { AgentFeaturedSection } from "./agent-featured-section"
-import { DialogCreateAgentTrigger } from "./dialog-create-agent/dialog-trigger-create-agent"
-import { UserAgentsSection } from "./user-agent-section"
-
-type AgentsPageProps = {
- curatedAgents: Agent[]
- userAgents: Agent[] | null
- userId: string | null
-}
-
-export function AgentsPage({
- curatedAgents,
- userAgents,
- userId,
-}: AgentsPageProps) {
- const [openAgentId, setOpenAgentId] = useState(null)
-
- const randomAgents = useMemo(() => {
- return curatedAgents
- .filter((agent) => agent.id !== openAgentId)
- .sort(() => Math.random() - 0.5)
- .slice(0, 4)
- }, [curatedAgents, openAgentId])
-
- const handleAgentClick = (agentId: string | null) => {
- setOpenAgentId(agentId)
- }
-
- return (
-
-
-
-
- Agents (experimental)
-
-
- Your every day AI assistant
-
-
- a growing set of personal AI agents, built for ideas, writing, and
- product work.
-
-
- Create an agent
-
- }
- />
-
-
-
-
-
-
- )
-}
diff --git a/app/components/agents/dialog-agent.tsx b/app/components/agents/dialog-agent.tsx
deleted file mode 100644
index 9f25135ee..000000000
--- a/app/components/agents/dialog-agent.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { useBreakpoint } from "@/app/hooks/use-breakpoint"
-import { AgentSummary } from "@/app/types/agent"
-import type { Tables } from "@/app/types/database.types"
-import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"
-import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"
-import { AgentCard } from "./agent-card"
-import { AgentDetail } from "./agent-detail"
-
-type DialogAgentProps = {
- id: string
- name: string
- description: string
- avatar_url?: string | null
- example_inputs: string[]
- className?: string
- isAvailable: boolean
- slug: string
- onAgentClick?: (agentId: string | null) => void
- isOpen: boolean
- onOpenChange: (open: boolean) => void
- randomAgents: AgentSummary[]
- trigger?: React.ReactNode
- system_prompt?: string | null
- tools?: string[] | null
- mcp_config?: Tables<"agents">["mcp_config"] | null
- isCardLight?: boolean
- creator_id?: string | null
-}
-
-export function DialogAgent({
- id,
- name,
- description,
- avatar_url,
- example_inputs,
- slug,
- system_prompt,
- className,
- isAvailable,
- onAgentClick,
- isOpen,
- onOpenChange,
- randomAgents,
- trigger = null,
- tools,
- mcp_config,
- isCardLight = false,
- creator_id,
-}: DialogAgentProps) {
- const isMobile = useBreakpoint(768)
-
- const handleOpenChange = (open: boolean) => {
- if (!isAvailable) {
- return
- }
-
- window.history.replaceState(null, "", `/agents/${slug}`)
- onOpenChange(open)
- }
-
- const defaultTrigger = (
- handleOpenChange(true)}
- tools={tools}
- mcp_config={mcp_config}
- isLight={isCardLight}
- />
- )
-
- const renderContent = () => (
-
- )
-
- if (isMobile) {
- return (
-
- {trigger || defaultTrigger}
-
- {renderContent()}
-
-
- )
- }
-
- return (
-
- )
-}
diff --git a/app/components/agents/dialog-create-agent/create-agent-form.tsx b/app/components/agents/dialog-create-agent/create-agent-form.tsx
deleted file mode 100644
index 1d2c1520d..000000000
--- a/app/components/agents/dialog-create-agent/create-agent-form.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-"use client"
-
-import { Badge } from "@/components/ui/badge"
-import { Button } from "@/components/ui/button"
-import { Input } from "@/components/ui/input"
-import { Label } from "@/components/ui/label"
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select"
-import { Textarea } from "@/components/ui/textarea"
-import { AlertCircle, Check, Github, X } from "lucide-react"
-import type React from "react"
-import { ToolsSection } from "./tools-section"
-
-export type AgentFormData = {
- name: string
- description: string
- systemPrompt: string
- mcp: "none" | "git-mcp"
- repository?: string
- tools: string[]
-}
-
-type CreateAgentFormProps = {
- formData: AgentFormData
- repository: string
- setRepository: (e: React.ChangeEvent) => void
- error: { [key: string]: string }
- isLoading: boolean
- handleInputChange: (
- e: React.ChangeEvent
- ) => void
- handleSelectChange: (value: string) => void
- handleToolsChange: (selectedTools: string[]) => void
- handleSubmit: (e: React.FormEvent) => Promise
- onClose: () => void
- isDrawer?: boolean
-}
-
-export function CreateAgentForm({
- formData,
- repository,
- setRepository,
- error,
- isLoading,
- handleInputChange,
- handleSelectChange,
- handleToolsChange,
- handleSubmit,
- onClose,
- isDrawer = false,
-}: CreateAgentFormProps) {
- return (
-
- {isDrawer && (
-
-
Create agent (experimental)
-
-
- )}
-
-
-
-
- Agents can use a system prompt and optionally connect to GitHub
- repos via git-mcp. More tools and MCP integrations are coming soon.
-
-
-
-
-
-
- )
-}
diff --git a/app/components/agents/dialog-create-agent/dialog-trigger-create-agent.tsx b/app/components/agents/dialog-create-agent/dialog-trigger-create-agent.tsx
deleted file mode 100644
index 4351de62c..000000000
--- a/app/components/agents/dialog-create-agent/dialog-trigger-create-agent.tsx
+++ /dev/null
@@ -1,200 +0,0 @@
-"use client"
-
-import { PopoverContentAuth } from "@/app/components/chat-input/popover-content-auth"
-import {
- Dialog,
- DialogContent,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "@/components/ui/dialog"
-import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"
-import { Popover, PopoverTrigger } from "@/components/ui/popover"
-import { toast } from "@/components/ui/toast"
-import { useAgent } from "@/lib/agent-store/provider"
-import { fetchClient } from "@/lib/fetch"
-import { API_ROUTE_CREATE_AGENT } from "@/lib/routes"
-import { useUser } from "@/lib/user-store/provider"
-import { useRouter } from "next/navigation"
-import type React from "react"
-import { useState } from "react"
-import { useBreakpoint } from "../../../hooks/use-breakpoint"
-import { AgentFormData, CreateAgentForm } from "./create-agent-form"
-
-export function DialogCreateAgentTrigger({
- trigger,
-}: {
- trigger: React.ReactNode
-}) {
- const { user } = useUser()
- const { refetchUserAgents } = useAgent()
- const isAuthenticated = !!user?.id
- const [open, setOpen] = useState(false)
- const [formData, setFormData] = useState({
- name: "",
- description: "",
- systemPrompt: "",
- mcp: "none",
- tools: [],
- })
- const [repository, setRepository] = useState("")
- const [error, setError] = useState<{ [key: string]: string }>({})
- const [isLoading, setIsLoading] = useState(false)
- const router = useRouter()
- const isMobile = useBreakpoint(768)
-
- const generateSystemPrompt = (owner: string, repo: string) => {
- return `You are a helpful GitHub assistant focused on the repository: ${owner}/${repo}.
-
-Use the available tools below to answer any questions. Always prefer using tools over guessing.
-
-Tools available for this repository:
-- \`fetch_${repo}_documentation\`: Fetch the entire documentation file.
-- \`search_${repo}_documentation\`: Semantically search the documentation.
-- \`search_${repo}_code\`: Search code with exact matches using the GitHub API.
-- \`fetch_generic_url_content\`: Fetch absolute URLs when referenced in the docs or needed for context.
-
-Never invent answers. Use tools and return what you find.`
- }
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault()
- const newErrors: { [key: string]: string } = {}
- if (!formData.name.trim()) newErrors.name = "Agent name is required"
- if (!formData.description.trim())
- newErrors.description = "Description is required"
- if (!formData.systemPrompt.trim())
- newErrors.systemPrompt = "System prompt is required"
- if (
- formData.mcp === "git-mcp" &&
- !/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repository)
- ) {
- newErrors.repository =
- 'Please enter a valid repository in the format "owner/repo"'
- }
- if (Object.keys(newErrors).length) return setError(newErrors)
-
- setIsLoading(true)
- try {
- if (formData.mcp === "git-mcp") {
- const response = await fetch(
- `https://api.github.com/repos/${repository}`
- )
- if (!response.ok) {
- setError({ repository: "Repository not found." })
- setIsLoading(false)
- return
- }
- formData.repository = repository
- }
-
- const [owner, repo] = repository.split("/")
-
- const res = await fetchClient(API_ROUTE_CREATE_AGENT, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- name: formData.name,
- description: formData.description,
- systemPrompt: formData.systemPrompt,
- avatar_url: repository ? `https://github.com/${owner}.png` : null,
- mcp_config: repository
- ? { server: `https://gitmcp.io/${owner}/${repo}`, variables: [] }
- : null,
- example_inputs: repository
- ? [
- "what does this repository do?",
- "how to install the project?",
- "how can I use this project?",
- "where is the main code located?",
- ]
- : null,
- tools: formData.tools,
- remixable: false,
- is_public: true,
- max_steps: 5,
- }),
- })
-
- if (!res.ok) throw new Error("Failed to create agent")
- const { agent } = await res.json()
- refetchUserAgents()
- setOpen(false)
- router.push(`/?agent=${agent.slug}`)
- } catch (err) {
- console.error(err)
- toast({
- title: "Error",
- description: "Could not create agent. Try again.",
- })
- } finally {
- setIsLoading(false)
- }
- }
-
- const content = (
- setRepository(e.target.value)}
- error={error}
- isLoading={isLoading}
- handleInputChange={(e) => {
- const { name, value } = e.target
- setFormData((f) => ({ ...f, [name]: value }))
- if (error[name]) setError((err) => ({ ...err, [name]: "" }))
- }}
- handleSelectChange={(value) => {
- setFormData((f) => ({ ...f, mcp: value as "none" | "git-mcp" }))
- if (value !== "git-mcp") setError((e) => ({ ...e, repository: "" }))
- if (value === "git-mcp" && /^[^/]+\/[^/]+$/.test(repository)) {
- const [owner, repo] = repository.split("/")
- setFormData((f) => ({
- ...f,
- systemPrompt: generateSystemPrompt(owner, repo),
- }))
- }
- }}
- handleToolsChange={(tools) => setFormData((f) => ({ ...f, tools }))}
- handleSubmit={handleSubmit}
- onClose={() => setOpen(false)}
- isDrawer={isMobile}
- />
- )
-
- if (!isAuthenticated) {
- return (
-
- {trigger}
-
-
- )
- }
-
- if (isMobile) {
- return (
-
- {trigger}
- {content}
-
- )
- }
-
- return (
-
- )
-}
diff --git a/app/components/agents/dialog-create-agent/tools-section.tsx b/app/components/agents/dialog-create-agent/tools-section.tsx
deleted file mode 100644
index a00e34457..000000000
--- a/app/components/agents/dialog-create-agent/tools-section.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-"use client"
-
-import { Label } from "@/components/ui/label"
-import { fetchClient } from "@/lib/fetch"
-import { getAllTools } from "@/lib/tools"
-import { cn } from "@/lib/utils"
-import { useQuery } from "@tanstack/react-query"
-import React, { useState } from "react"
-
-const isDev = process.env.NODE_ENV === "development"
-
-type ToolsSectionProps = {
- onSelectTools: (selectedTools: string[]) => void
-}
-
-export function ToolsSection({ onSelectTools }: ToolsSectionProps) {
- const [selectedTools, setSelectedTools] = useState([])
- const tools = getAllTools()
-
- const { data: availableTools } = useQuery({
- queryKey: ["available-tools"],
- queryFn: async () => {
- if (!isDev) return []
- const res = await fetchClient("/api/tools-available")
- const json = await res.json()
- return json.available || []
- },
- enabled: isDev,
- })
-
- const handleToolToggle = (toolId: string) => {
- const newSelection = selectedTools.includes(toolId)
- ? selectedTools.filter((id) => id !== toolId)
- : [...selectedTools, toolId]
-
- setSelectedTools(newSelection)
- onSelectTools(newSelection)
- }
-
- // Development mode required
- if (!isDev) {
- return (
-
-
-
-
- Tools are only available in development mode.
-
-
-
- )
- }
-
- return (
-
-
-
-
- Select tools to enable for this agent. Tools are used to interact with
- the world.
-
-
-
-
- {tools.map((tool) => {
- const isSelected = selectedTools.includes(tool.id)
- const isAvailable = availableTools?.includes(tool.id)
-
- return (
-
isAvailable && handleToolToggle(tool.id)}
- >
-
-
-
-
-
-
-
{tool.id}
-
-
- {tool.description}
-
-
-
-
- )
- })}
-
-
- {selectedTools.length > 0 && (
-
-
Selected tools:
-
- {selectedTools
- .map((toolId) => {
- const tool = tools.find((t) => t.id === toolId)
- return tool?.description || toolId
- })
- .join(", ")}
-
-
- )}
-
- {tools.length === 0 && (
-
- )}
-
- )
-}
diff --git a/app/components/agents/research-section.tsx b/app/components/agents/research-section.tsx
deleted file mode 100644
index 4d14d0b2e..000000000
--- a/app/components/agents/research-section.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { AgentSummary } from "@/app/types/agent"
-import { ArrowUpRight } from "@phosphor-icons/react"
-import Image from "next/image"
-import { DialogAgent } from "./dialog-agent"
-
-type ResearchSectionProps = {
- researchAgent: AgentSummary
- handleAgentClick: (agentId: string | null) => void
- openAgentId: string | null
- setOpenAgentId: (agentId: string | null) => void
- randomAgents: AgentSummary[]
-}
-export function ResearchSection({
- researchAgent,
- handleAgentClick,
- openAgentId,
- setOpenAgentId,
- randomAgents,
-}: ResearchSectionProps) {
- return (
-
-
Research
-
- Backed by real-time web search. Ideal for deep, accurate research.
-
-
setOpenAgentId(open ? researchAgent.id : null)}
- randomAgents={randomAgents}
- system_prompt={researchAgent.system_prompt}
- tools={researchAgent.tools || []}
- mcp_config={researchAgent.mcp_config}
- trigger={
-
- }
- />
-
- )
-}
diff --git a/app/components/agents/user-agent-section.tsx b/app/components/agents/user-agent-section.tsx
deleted file mode 100644
index f5f5a6d86..000000000
--- a/app/components/agents/user-agent-section.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { Agent } from "@/app/types/agent"
-import { Button } from "@/components/ui/button"
-import { Card, CardContent } from "@/components/ui/card"
-import Link from "next/link"
-import { DialogAgent } from "./dialog-agent"
-import { DialogCreateAgentTrigger } from "./dialog-create-agent/dialog-trigger-create-agent"
-
-type UserAgentsSectionProps = {
- agents: Agent[] | null
- userId?: string | null
- handleAgentClick: (agentId: string | null) => void
- openAgentId: string | null
- setOpenAgentId: (agentId: string | null) => void
- moreAgents: Agent[]
-}
-
-export function UserAgentsSection({
- agents,
- userId,
- handleAgentClick,
- openAgentId,
- setOpenAgentId,
- moreAgents,
-}: UserAgentsSectionProps) {
- const hasUserAgents = agents && agents.length > 0
-
- // Not authenticated
- if (!userId) {
- return (
-
-
-
- Want to create your own agents?
-
-
- Sign in to create and manage custom AI agents
-
-
-
-
- )
- }
-
- // Authenticated but no agents
- if (userId && !hasUserAgents) {
- return (
-
-
-
- You haven't created any agents yet
-
-
- Create your first custom agent to get started
-
- Create an agent}
- />
-
-
- )
- }
-
- // Authenticated with agents
- return (
-
-
Your agents
-
- {agents?.map((agent) => (
- setOpenAgentId(open ? agent.id : null)}
- randomAgents={moreAgents}
- slug={agent.slug}
- system_prompt={agent.system_prompt}
- tools={agent.tools}
- mcp_config={agent.mcp_config}
- creator_id={agent.creator_id}
- />
- ))}
-
-
- )
-}
diff --git a/app/components/chat-input/agent-command.tsx b/app/components/chat-input/agent-command.tsx
deleted file mode 100644
index 64bf2e924..000000000
--- a/app/components/chat-input/agent-command.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-"use client"
-
-import useClickOutside from "@/app/hooks/use-click-outside"
-import { Agent } from "@/app/types/agent"
-import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
-import { isSupabaseEnabled } from "@/lib/supabase/config"
-import { cn } from "@/lib/utils"
-import { Cube, Plus } from "@phosphor-icons/react"
-import { useRef } from "react"
-import { DialogCreateAgentTrigger } from "../agents/dialog-create-agent/dialog-trigger-create-agent"
-
-type AgentCommandProps = {
- isOpen: boolean
- searchTerm: string
- onSelect: (agent: Agent) => void
- onClose: () => void
- activeIndex: number
- onActiveIndexChange: (index: number) => void
- onCreateNewAgent?: () => void
- curatedAgents: Agent[]
- userAgents: Agent[]
-}
-
-export function AgentCommand({
- isOpen,
- searchTerm,
- onSelect,
- onClose,
- activeIndex,
- onActiveIndexChange,
- onCreateNewAgent = () => {},
- curatedAgents,
- userAgents,
-}: AgentCommandProps) {
- const containerRef = useRef(null)
-
- // Handle clicks outside using the custom hook
- useClickOutside(containerRef, onClose)
-
- // Ref callback to handle scrolling into view
- const activeItemRef = (element: HTMLLIElement | null) => {
- if (element && isOpen) {
- element.scrollIntoView({ block: "nearest" })
- }
- }
-
- if (!isSupabaseEnabled) {
- return null
- }
-
- // Filter agents based on search term
- const filteredAgents = searchTerm
- ? [...curatedAgents, ...userAgents].filter((agent) =>
- agent.name.toLowerCase().includes(searchTerm.toLowerCase())
- )
- : [...curatedAgents, ...userAgents]
-
- // Helper function to check if an agent is curated
- const isCuratedAgent = (agentId: string) => {
- return curatedAgents.some((curatedAgent) => curatedAgent.id === agentId)
- }
-
- if (!isOpen) return null
-
- return (
-
-
- Agents (experimental)
-
- {filteredAgents.length === 0 ? (
-
No agent found.
- ) : (
-
- {filteredAgents.map((agent, index) => {
- return (
- - onActiveIndexChange(index)}
- onClick={() => onSelect(agent)}
- >
-
- {agent.avatar_url ? (
-
-
-
- {agent.name.charAt(0).toUpperCase()}
-
-
- ) : (
-
-
-
- )}
-
- {agent.name}
-
- {agent.description}
-
-
- {isCuratedAgent(agent.id) && (
-
- Zola
-
- )}
-
-
- )
- })}
-
- )}
-
-
-
- }
- />
-
- )
-}
diff --git a/app/components/chat-input/agents.tsx b/app/components/chat-input/agents.tsx
deleted file mode 100644
index dae06c166..000000000
--- a/app/components/chat-input/agents.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-"use client"
-
-import type { AgentsSuggestions } from "@/app/types/agent"
-import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
-import { Button } from "@/components/ui/button"
-import {
- HoverCard,
- HoverCardContent,
- HoverCardTrigger,
-} from "@/components/ui/hover-card"
-import { TRANSITION_SUGGESTIONS } from "@/lib/motion"
-import { cn } from "@/lib/utils"
-import { motion } from "motion/react"
-import { useRouter, useSearchParams } from "next/navigation"
-import { memo, useState } from "react"
-
-type ButtonAgentProps = {
- label: string
- avatarUrl: string
- description: string
- slug: string
- isActive: boolean
- onSelect: (slug: string) => void
-}
-
-const ButtonAgent = memo(function ButtonAgent({
- label,
- avatarUrl,
- description,
- slug,
- isActive,
- onSelect,
-}: ButtonAgentProps) {
- return (
-
-
-
-
-
- {description}
-
-
- )
-})
-
-type AgentsProps = {
- sugestedAgents: AgentsSuggestions[]
-}
-
-export const Agents = memo(function Agents({ sugestedAgents }: AgentsProps) {
- const router = useRouter()
- const params = useSearchParams()
- const agentSlug = params.get("agent")
- const [selectedAgent, setSelectedAgent] = useState(agentSlug)
-
- const handleAgentSelect = (slug: string) => {
- const newSelectedAgent = selectedAgent === slug ? null : slug
- setSelectedAgent(newSelectedAgent)
-
- if (newSelectedAgent) {
- router.push(`/?agent=${newSelectedAgent}`)
- } else {
- router.push(`/`)
- }
- }
-
- return (
-
- {sugestedAgents?.map((agent, index) => (
-
-
-
- ))}
-
- )
-})
diff --git a/app/components/chat-input/chat-input.tsx b/app/components/chat-input/chat-input.tsx
index c3a73044a..1f00d2e7e 100644
--- a/app/components/chat-input/chat-input.tsx
+++ b/app/components/chat-input/chat-input.tsx
@@ -1,6 +1,5 @@
"use client"
-import { useAgentCommand } from "@/app/components/chat-input/use-agent-command"
import { ModelSelector } from "@/components/common/model-selector/base"
import {
PromptInput,
@@ -9,16 +8,13 @@ import {
PromptInputTextarea,
} from "@/components/prompt-kit/prompt-input"
import { Button } from "@/components/ui/button"
-import { useAgent } from "@/lib/agent-store/provider"
import { getModelInfo } from "@/lib/models"
import { ArrowUp, Stop, Warning } from "@phosphor-icons/react"
import React, { useCallback, useEffect, useMemo } from "react"
import { PromptSystem } from "../suggestions/prompt-system"
-import { AgentCommand } from "./agent-command"
import { ButtonFileUpload } from "./button-file-upload"
import { ButtonSearch } from "./button-search"
import { FileList } from "./file-list"
-import { SelectedAgent } from "./selected-agent"
type ChatInputProps = {
value: string
@@ -58,15 +54,6 @@ export function ChatInput({
setEnableSearch,
enableSearch,
}: ChatInputProps) {
- const { currentAgent, curatedAgents, userAgents } = useAgent()
-
- const agentCommand = useAgentCommand({
- value,
- onValueChange,
- agents: [...(curatedAgents || []), ...(userAgents || [])],
- defaultAgent: currentAgent,
- })
-
const selectModelConfig = getModelInfo(selectedModel)
const hasToolSupport = Boolean(selectModelConfig?.tools)
const hasSearchSupport = Boolean(selectModelConfig?.webSearch)
@@ -87,9 +74,6 @@ export function ChatInput({
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
- // First process agent command related key handling
- agentCommand.handleKeyDown(e)
-
if (isSubmitting) {
e.preventDefault()
return
@@ -100,7 +84,7 @@ export function ChatInput({
return
}
- if (e.key === "Enter" && !e.shiftKey && !agentCommand.showAgentCommand) {
+ if (e.key === "Enter" && !e.shiftKey) {
if (isOnlyWhitespace(value)) {
return
}
@@ -109,7 +93,7 @@ export function ChatInput({
onSend()
}
},
- [agentCommand, isSubmitting, onSend, status, value]
+ [isSubmitting, onSend, status, value]
)
const handlePaste = useCallback(
@@ -152,13 +136,6 @@ export function ChatInput({
[isUserAuthenticated, onFileUpload]
)
- useEffect(() => {
- const el = agentCommand.textareaRef.current
- if (!el) return
- el.addEventListener("paste", handlePaste)
- return () => el.removeEventListener("paste", handlePaste)
- }, [agentCommand.textareaRef, handlePaste])
-
useMemo(() => {
if (!hasSearchSupport && enableSearch) {
setEnableSearch?.(false)
@@ -179,32 +156,13 @@ export function ChatInput({
className="bg-popover relative z-10 p-0 pt-1 shadow-xs backdrop-blur-xl"
maxHeight={200}
value={value}
- onValueChange={agentCommand.handleValueChange}
+ onValueChange={onValueChange}
>
- {agentCommand.showAgentCommand && (
-
- )}
-
@@ -226,15 +184,6 @@ export function ChatInput({
isAuthenticated={isUserAuthenticated}
/>
) : null}
- {currentAgent && !hasToolSupport && (
-
-
-
- {selectedModel} does not support tools. Agents may not work
- as expected.
-
-
- )}
- Add files, use more models, agents, and more.
+ Add files, use more models, BYO API keys, and more.