diff --git a/src/components/agents/AgentsPanel.tsx b/src/components/agents/AgentsPanel.tsx deleted file mode 100644 index 61924b6..0000000 --- a/src/components/agents/AgentsPanel.tsx +++ /dev/null @@ -1,1121 +0,0 @@ -"use client"; - -import { useCallback, useEffect, useMemo, useState } from 'react'; -import RAHChat from './RAHChat'; -import QuickAddInput from './QuickAddInput'; -import QuickAddStatus from './QuickAddStatus'; -import { Zap, Flame, Minimize2 } from 'lucide-react'; -import { Node } from '@/types/database'; -import { parseAndRenderContent } from '@/components/helpers/NodeLabelRenderer'; - -// Stub type for delegation (delegation system removed in rah-light) -type AgentDelegation = { - id: number; - sessionId: string; - task: string; - status: 'queued' | 'in_progress' | 'completed' | 'failed'; - summary?: string | null; - agentType: string; - createdAt: string; - updatedAt: string; -}; - -interface AgentsPanelProps { - openTabsData: Node[]; - activeTabId: number | null; - activeDimension?: string | null; - onNodeClick?: (nodeId: number) => void; - onCollapse?: () => void; -} - -type ActiveTab = 'ra-h' | 'workflows' | string; // 'ra-h', 'workflows', or delegation sessionId -type Mode = 'quickadd' | 'session'; - -export default function AgentsPanel({ openTabsData, activeTabId, activeDimension, onNodeClick, onCollapse }: AgentsPanelProps) { - const [delegationsMap, setDelegationsMap] = useState>({}); - const [activeAgentTab, setActiveAgentTab] = useState('ra-h'); - const [mode, setMode] = useState('quickadd'); - const [rahMode, setRahMode] = useState<'easy' | 'hard'>('easy'); - const [modelDropdownOpen, setModelDropdownOpen] = useState(false); - // Lift messages state to prevent losing it on tab switch - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [rahMessages, setRahMessages] = useState([]); - - // Store delegation messages per sessionId - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [delegationMessages, setDelegationMessages] = useState>({}); - - const getDelegationMessages = useCallback((sessionId: string) => { - return delegationMessages[sessionId] || []; - }, [delegationMessages]); - - const setDelegationMessagesFor = useCallback((sessionId: string) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (updater: (prev: any[]) => any[]) => { - setDelegationMessages(prev => ({ - ...prev, - [sessionId]: updater(prev[sessionId] || []) - })); - }; - }, []); - - const upsertDelegation = useCallback((delegation: AgentDelegation) => { - setDelegationsMap((prev) => ({ - ...prev, - [delegation.sessionId]: delegation, - })); - }, []); - - useEffect(() => { - let cancelled = false; - const loadExisting = async () => { - try { - const response = await fetch('/api/rah/delegations?status=active&includeCompleted=true'); - if (!response.ok) return; - const data = await response.json(); - if (!Array.isArray(data.delegations)) return; - - setDelegationsMap((prev) => { - if (cancelled) return prev; - const next = { ...prev }; - for (const delegation of data.delegations as AgentDelegation[]) { - next[delegation.sessionId] = delegation; - } - return next; - }); - } catch (error) { - console.error('[AgentsPanel] Failed to load delegations:', error); - } - }; - - loadExisting(); - - return () => { - cancelled = true; - }; - }, []); - - useEffect(() => { - if (typeof window === 'undefined') return; - const stored = window.localStorage.getItem('rah-mode'); - if (stored === 'easy' || stored === 'hard') { - setRahMode(stored); - } - }, []); - - useEffect(() => { - if (typeof window === 'undefined') return; - window.localStorage.setItem('rah-mode', rahMode); - }, [rahMode]); - - useEffect(() => { - const handler = (event: Event) => { - const detail = (event as CustomEvent<{ mode: 'easy' | 'hard' }>).detail; - if (detail?.mode) { - setRahMode(detail.mode); - } - }; - const quickAddHandler = () => setMode('quickadd'); - window.addEventListener('rah:mode-toggle', handler as EventListener); - window.addEventListener('rah:switch-quickadd', quickAddHandler); - return () => { - window.removeEventListener('rah:mode-toggle', handler as EventListener); - window.removeEventListener('rah:switch-quickadd', quickAddHandler); - }; - }, []); - - - useEffect(() => { - const handleCreated = (event: Event) => { - const detail = (event as CustomEvent<{ delegation: AgentDelegation }>).detail; - console.log('[AgentsPanel] Delegation created:', detail?.delegation); - if (detail?.delegation) upsertDelegation(detail.delegation); - }; - const handleUpdated = (event: Event) => { - const detail = (event as CustomEvent<{ delegation: AgentDelegation }>).detail; - console.log('[AgentsPanel] Delegation updated:', detail?.delegation); - if (detail?.delegation) upsertDelegation(detail.delegation); - }; - - window.addEventListener('delegations:created', handleCreated as EventListener); - window.addEventListener('delegations:updated', handleUpdated as EventListener); - return () => { - window.removeEventListener('delegations:created', handleCreated as EventListener); - window.removeEventListener('delegations:updated', handleUpdated as EventListener); - }; - }, [upsertDelegation]); - - const delegations = useMemo(() => Object.values(delegationsMap), [delegationsMap]); - - const orderedDelegations = useMemo(() => { - const statusWeight = (status: AgentDelegation['status']) => { - switch (status) { - case 'queued': - return 0; - case 'in_progress': - return 1; - case 'completed': - return 2; - case 'failed': - default: - return 3; - } - }; - - // No staleness filtering - delegations persist until user closes them - const ordered = delegations.sort((a, b) => { - const diff = statusWeight(a.status) - statusWeight(b.status); - if (diff !== 0) return diff; - return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(); - }); - - const wise = ordered.filter((d) => d.agentType === 'wise-rah'); - const mini = ordered.filter((d) => d.agentType !== 'wise-rah'); - - return [...wise, ...mini]; - }, [delegations]); - - // Don't auto-switch tabs - user stays in ra-h to see responses - - const selectedDelegation = orderedDelegations.find(d => d.sessionId === activeAgentTab); - - const handleQuickAddSubmit = async ({ input, mode, description }: { input: string; mode: 'link' | 'note' | 'chat'; description?: string }) => { - try { - const response = await fetch('/api/quick-add', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ input, mode, description }) - }); - - if (!response.ok) { - const data = await response.json(); - throw new Error(data.error || 'Failed to submit Quick Add'); - } - } catch (error) { - console.error('[AgentsPanel] Quick Add error:', error); - } - }; - - const handleDelegationClick = (sessionId: string) => { - setMode('session'); - setActiveAgentTab(sessionId); - }; - - return ( -
- {/* Mode Header */} - {mode === 'quickadd' ? ( -
- {/* Top Bar - collapse button + Add Stuff */} -
- {onCollapse && ( - - )} - {/* Add Stuff - top right */} -
- -
-
- - {/* Center Section - Start button centered */} -
- -
-
- ) : null} - - {/* Tab Bar (only show in session mode) */} - {mode === 'session' && ( -
- {/* Collapse button - first item */} - {onCollapse && ( - - )} - {/* Capture button - positioned at far right */} -
- -
- - {/* RA-H Main Tab */} - - - {/* Workflows Tab */} - {orderedDelegations.length > 0 && ( - - )} -
- )} - - {/* Active Panel */} -
- {mode === 'quickadd' ? ( -
- -
- ) : ( - <> - {/* Keep RAHChat always mounted, just hide when not active */} -
- -
- - {/* Workflows list view */} - {activeAgentTab === 'workflows' && ( -
- setActiveAgentTab(sessionId)} - onDeleteDelegation={async (sessionId) => { - try { - await fetch(`/api/rah/delegations/${sessionId}`, { method: 'DELETE' }); - } catch (error) { - console.error(`Failed to delete delegation ${sessionId}:`, error); - } - setDelegationsMap((prev) => { - const { [sessionId]: _ignored, ...rest } = prev; - return rest; - }); - setDelegationMessages((prev) => { - const { [sessionId]: _ignored, ...rest } = prev; - return rest; - }); - }} - onNodeClick={onNodeClick} - /> -
- )} - - {/* Show delegation detail when a specific delegation is selected */} - {selectedDelegation && activeAgentTab !== 'ra-h' && activeAgentTab !== 'workflows' && ( -
- {/* Show summary view if completed/failed with no messages */} - {(selectedDelegation.status === 'completed' || selectedDelegation.status === 'failed') - && getDelegationMessages(selectedDelegation.sessionId).length === 0 ? ( - setActiveAgentTab('workflows')} - onNodeClick={onNodeClick} - /> - ) : ( - setActiveAgentTab('workflows')} - /> - )} -
- )} - - )} -
- -
- ); -} - -// Summary view for completed delegations with no messages -function DelegationSummaryView({ delegation, onBack, onNodeClick }: { delegation: AgentDelegation; onBack?: () => void; onNodeClick?: (nodeId: number) => void }) { - const isSuccess = delegation.status === 'completed'; - const statusColor = isSuccess ? '#22c55e' : '#ff6b6b'; - const statusLabel = isSuccess ? 'Completed' : 'Failed'; - - return ( -
- {/* Header with back button */} -
- {onBack && ( - - )} -
- - {statusLabel} - - - {new Date(delegation.updatedAt).toLocaleString()} - -
- - {/* Task */} -
-
- Task -
-
- {delegation.task} -
-
- - {/* Summary */} - {delegation.summary && ( -
-
- Result -
-
- {parseAndRenderContent(delegation.summary || '', onNodeClick)} -
-
- )} - - {/* No summary fallback */} - {!delegation.summary && ( -
- No details available -
- )} -
- ); -} - -// Workflows list view - shows all delegations in a nice list -function WorkflowsListView({ - delegations, - onSelectDelegation, - onDeleteDelegation, - onNodeClick -}: { - delegations: AgentDelegation[]; - onSelectDelegation: (sessionId: string) => void; - onDeleteDelegation: (sessionId: string) => void; - onNodeClick?: (nodeId: number) => void; -}) { - const activeDelegations = delegations.filter(d => d.status === 'queued' || d.status === 'in_progress'); - const completedDelegations = delegations.filter(d => d.status === 'completed' || d.status === 'failed'); - - const getStatusInfo = (delegation: AgentDelegation) => { - const isWiseRAH = delegation.agentType === 'wise-rah'; - let color = '#6b6b6b'; - let label = 'Queued'; - - if (delegation.status === 'in_progress') { - color = isWiseRAH ? '#8b5cf6' : '#22c55e'; - label = 'Running'; - } else if (delegation.status === 'completed') { - color = '#22c55e'; - label = 'Done'; - } else if (delegation.status === 'failed') { - color = '#ff6b6b'; - label = 'Failed'; - } - - return { color, label }; - }; - - return ( -
- {/* Header */} -
- - Workflows - - {activeDelegations.length > 0 && ( - - {activeDelegations.length} running - - )} -
- - {/* List */} -
- {delegations.length === 0 ? ( -
- No workflows yet. Use Quick Capture or ask RA-H to run a workflow. -
- ) : ( -
- {/* Active workflows first */} - {activeDelegations.map((delegation) => { - const { color, label } = getStatusInfo(delegation); - return ( - onSelectDelegation(delegation.sessionId)} - onDelete={() => onDeleteDelegation(delegation.sessionId)} - onNodeClick={onNodeClick} - /> - ); - })} - - {/* Divider if both active and completed exist */} - {activeDelegations.length > 0 && completedDelegations.length > 0 && ( -
- )} - - {/* Completed workflows */} - {completedDelegations.map((delegation) => { - const { color, label } = getStatusInfo(delegation); - return ( - onSelectDelegation(delegation.sessionId)} - onDelete={() => onDeleteDelegation(delegation.sessionId)} - onNodeClick={onNodeClick} - /> - ); - })} -
- )} -
-
- ); -} - -// Individual workflow card -function WorkflowCard({ - delegation, - statusColor, - statusLabel, - onSelect, - onDelete, - onNodeClick -}: { - delegation: AgentDelegation; - statusColor: string; - statusLabel: string; - onSelect: () => void; - onDelete: () => void; - onNodeClick?: (nodeId: number) => void; -}) { - const isActive = delegation.status === 'in_progress' || delegation.status === 'queued'; - - return ( -
{ - e.currentTarget.style.background = '#1a1a1a'; - e.currentTarget.style.borderColor = '#2a2a2a'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.background = '#151515'; - e.currentTarget.style.borderColor = '#1f1f1f'; - }} - > - {/* Top row: status + time + delete */} -
-
- - {statusLabel} - - - {new Date(delegation.createdAt).toLocaleTimeString()} - - -
- - {/* Task description */} -
- {delegation.task} -
- - {/* Summary preview if completed */} - {delegation.summary && delegation.status === 'completed' && ( -
- {parseAndRenderContent(delegation.summary, onNodeClick)} -
- )} -
- ); -} - -// Delegation detail view with back button - -function DelegationDetailView({ - delegation, - openTabsData, - activeTabId, - activeDimension, - onNodeClick, - delegations, - messages, - setMessages, - onBack -}: { - delegation: AgentDelegation; - openTabsData: Node[]; - activeTabId: number | null; - activeDimension?: string | null; - onNodeClick?: (nodeId: number) => void; - delegations: AgentDelegation[]; - messages: any[]; - setMessages: (updater: (prev: any[]) => any[]) => void; - onBack: () => void; -}) { - return ( -
- {/* Back button header */} -
- - - | - - - {delegation.status === 'in_progress' ? 'Running' : delegation.status} - -
- - {/* RAHChat for streaming */} -
- -
-
- ); -} diff --git a/src/components/agents/MiniRAHPanel.tsx b/src/components/agents/MiniRAHPanel.tsx deleted file mode 100644 index 359661e..0000000 --- a/src/components/agents/MiniRAHPanel.tsx +++ /dev/null @@ -1,249 +0,0 @@ -import { ReactNode } from 'react'; - -// Stub type for delegation (delegation system removed in rah-light) -type AgentDelegation = { - id: number; - sessionId: string; - task: string; - context: string[]; - status: 'queued' | 'in_progress' | 'completed' | 'failed'; - summary?: string | null; - agentType: string; - createdAt: string; - updatedAt: string; -}; - -interface MiniRAHPanelProps { - delegation: AgentDelegation; - onNodeClick?: (nodeId: number) => void; -} - -const statusPalette: Record = { - queued: { border: '#1f3a5f', badge: '#5c9aff' }, - in_progress: { border: '#3b5f2a', badge: '#8bd450' }, - completed: { border: '#2a2a2a', badge: '#6b6b6b' }, - failed: { border: '#5f2a2a', badge: '#ff6b6b' }, -}; - -const NODE_LINK_REGEX = /\[NODE:(\d+):"([^"]+)"\]/g; - -function formatStatus(status: string) { - switch (status) { - case 'queued': - return 'Queued'; - case 'in_progress': - return 'Working'; - case 'completed': - return 'Completed'; - case 'failed': - return 'Failed'; - default: - return status; - } -} - -function renderNodeAwareLine(text: string, onNodeClick?: (nodeId: number) => void): ReactNode { - const parts: ReactNode[] = []; - let lastIndex = 0; - - text.replace(NODE_LINK_REGEX, (match, id, title, offset) => { - if (offset > lastIndex) { - parts.push({text.slice(lastIndex, offset)}); - } - - const nodeId = Number(id); - const handleClick = () => { - if (onNodeClick) onNodeClick(nodeId); - }; - - parts.push( - - ); - - lastIndex = offset + match.length; - return match; - }); - - if (lastIndex < text.length) { - parts.push({text.slice(lastIndex)}); - } - - return <>{parts}; -} - -export default function MiniRAHPanel({ delegation, onNodeClick }: MiniRAHPanelProps) { - const palette = statusPalette[delegation.status] ?? statusPalette.queued; - const summaryLines = delegation.summary ? delegation.summary.split('\n').filter(Boolean) : []; - - return ( -
-
- - MINI RA-H - · {formatStatus(delegation.status)} - {new Date(delegation.updatedAt).toLocaleTimeString()} -
- -
-

Task

-

{delegation.task}

-
- - {delegation.context.length > 0 && ( -
-

Context

-
    - {delegation.context.map((item, idx) => ( -
  • {renderNodeAwareLine(item, onNodeClick)}
  • - ))} -
-
- )} - - {summaryLines.length > 0 && ( -
-

Summary

-
- {summaryLines.map((line, idx) => ( -

{renderNodeAwareLine(line, onNodeClick)}

- ))} -
-
- )} - - -
- ); -} diff --git a/src/components/agents/WiseRAHPanel.tsx b/src/components/agents/WiseRAHPanel.tsx deleted file mode 100644 index b6cd884..0000000 --- a/src/components/agents/WiseRAHPanel.tsx +++ /dev/null @@ -1,340 +0,0 @@ -import { Fragment, ReactNode, useMemo } from 'react'; - -// Stub type for delegation (delegation system removed in rah-light) -type AgentDelegation = { - id: number; - sessionId: string; - task: string; - context: string[]; - status: 'queued' | 'in_progress' | 'completed' | 'failed'; - summary?: string | null; - agentType: string; - createdAt: string; - updatedAt: string; -}; - -const statusPalette: Record = { - queued: { border: '#3a2f5f', badge: '#a78bfa' }, - in_progress: { border: '#4a3a6f', badge: '#8b5cf6' }, - completed: { border: '#2a2a2a', badge: '#6b6b6b' }, - failed: { border: '#5f2a2a', badge: '#ff6b6b' }, -}; - -const NODE_LINK_REGEX = /\[NODE:(\d+):"([^"]+)"\]/g; -const SUMMARY_HEADERS = ['Task', 'Actions', 'Result', 'Nodes', 'Follow-up']; - -interface WiseRAHPanelProps { - delegation: AgentDelegation; - onNodeClick?: (nodeId: number) => void; -} - -function formatStatus(status: string) { - switch (status) { - case 'queued': - return 'Queued'; - case 'in_progress': - return 'Planning'; - case 'completed': - return 'Completed'; - case 'failed': - return 'Failed'; - default: - return status; - } -} - -function renderNodeAwareText(text: string, onNodeClick?: (nodeId: number) => void): ReactNode { - const segments: ReactNode[] = []; - let lastIndex = 0; - - text.replace(NODE_LINK_REGEX, (match, id, title, offset) => { - if (offset > lastIndex) { - segments.push({text.slice(lastIndex, offset)}); - } - const nodeId = Number(id); - const handleClick = () => { - if (onNodeClick) { - onNodeClick(nodeId); - } - }; - segments.push( - - ); - lastIndex = offset + match.length; - return match; - }); - - if (lastIndex < text.length) { - segments.push({text.slice(lastIndex)}); - } - - return <>{segments}; -} - -function parseSummary(summary: string) { - const lines = summary.split('\n'); - const sections: Array<{ title: string; lines: string[] }> = []; - let current: { title: string; lines: string[] } | null = null; - - for (const rawLine of lines) { - const line = rawLine.trim(); - if (!line) continue; - - const header = SUMMARY_HEADERS.find(h => line.toLowerCase().startsWith(`${h.toLowerCase()}:`)); - if (header) { - const content = line.slice(header.length + 1).trim(); - current = { title: header, lines: [] }; - if (content) current.lines.push(content); - sections.push(current); - } else if (current) { - current.lines.push(line); - } else { - if (!current) { - current = { title: 'Summary', lines: [] }; - sections.push(current); - } - current.lines.push(line); - } - } - - return sections; -} - -function renderSectionLines(lines: string[], onNodeClick?: (nodeId: number) => void) { - const hasBullet = lines.some(line => /^[-*•]/.test(line.trim())); - - if (hasBullet) { - return ( -
    - {lines.map((line, idx) => { - const text = line.replace(/^[-*•]\s*/, '').trim(); - return ( -
  • - {renderNodeAwareText(text, onNodeClick)} -
  • - ); - })} -
- ); - } - - return ( -
- {lines.map((line, idx) => ( -

{renderNodeAwareText(line, onNodeClick)}

- ))} -
- ); -} - -export default function WiseRAHPanel({ delegation, onNodeClick }: WiseRAHPanelProps) { - const palette = statusPalette[delegation.status] ?? statusPalette.queued; - - const parsedSummary = useMemo(() => { - if (!delegation.summary) return []; - return parseSummary(delegation.summary); - }, [delegation.summary]); - - return ( -
-
- - WISE RA-H - · {formatStatus(delegation.status)} - {new Date(delegation.updatedAt).toLocaleTimeString()} -
- -
-

Goal

-

{delegation.task}

-
- - {delegation.context.length > 0 && ( -
-

Context

-
    - {delegation.context.map((item, idx) => ( -
  • {renderNodeAwareText(item, onNodeClick)}
  • - ))} -
-
- )} - - {parsedSummary.length > 0 && ( -
-

Summary

-
- {parsedSummary.map(section => ( -
-
{section.title}
- {renderSectionLines(section.lines, onNodeClick)} -
- ))} -
-
- )} - - -
- ); -}