// Claude Desktop — Event-Bridge // Empfängt Events vom Tauri-Backend und aktualisiert die Stores import { listen, type UnlistenFn } from '@tauri-apps/api/event'; import { agents, toolCalls, messages, isProcessing, addMessage, addAgent, updateAgentStatus, addToolCall, completeToolCall, clearAll, currentModel, sessionStats } from './app'; // Event-Typen vom Backend interface AgentEvent { id: string; type?: string; task?: string; code?: number; } interface ToolEvent { id: string; tool?: string; input?: Record; output?: string; success?: boolean; } interface TextEvent { text: string; } interface ResultEvent { cost?: number; tokens?: { input: number; output: number; }; session_id?: string; model?: string; } // Listener-Handles let listeners: UnlistenFn[] = []; // Streaming: ID der aktuellen Live-Nachricht let streamingMessageId: string | null = null; // Events initialisieren export async function initEventListeners(): Promise { console.log('🎧 Initialisiere Event-Listener...'); await cleanupEventListeners(); // Bridge bereit listeners.push( await listen('bridge-ready', () => { console.log('✅ Bridge bereit'); }) ); // Agent gestartet listeners.push( await listen('agent-started', (event) => { const { id, type, task } = event.payload; console.log('🤖 Agent gestartet:', id, type); addAgent(mapAgentType(type || 'main'), task || 'Verarbeite...'); isProcessing.set(true); // Leere Streaming-Nachricht anlegen streamingMessageId = crypto.randomUUID(); messages.update((msgs) => [ ...msgs, { id: streamingMessageId!, role: 'assistant', content: '', timestamp: new Date(), agentId: id } ]); }) ); // Agent gestoppt listeners.push( await listen('agent-stopped', (event) => { const { id } = event.payload; console.log('⏹️ Agent gestoppt:', id); updateAgentStatus(id, 'stopped'); streamingMessageId = null; // Prüfen ob noch Agents aktiv agents.update((ags) => { const stillActive = ags.some((a) => a.status === 'active'); if (!stillActive) { isProcessing.set(false); } return ags; }); }) ); // Alle Agents gestoppt listeners.push( await listen('all-stopped', () => { console.log('⏹️ Alle Agents gestoppt'); agents.update((ags) => ags.map((a) => ({ ...a, status: 'stopped' as const }))); isProcessing.set(false); streamingMessageId = null; }) ); // Tool Start listeners.push( await listen('tool-start', (event) => { const { tool, input } = event.payload; console.log('🔧 Tool Start:', tool); agents.update((ags) => { const activeAgent = ags.find((a) => a.status === 'active'); if (activeAgent) { addToolCall(activeAgent.id, tool || 'unknown', input || {}); } return ags; }); }) ); // Tool Ende listeners.push( await listen('tool-end', (event) => { const { id, success, output } = event.payload; console.log('✅ Tool Ende:', id, success ? 'OK' : 'FEHLER'); completeToolCall(id, output, !success); }) ); // Text-Streaming — live in die aktuelle Nachricht schreiben listeners.push( await listen('claude-text', (event) => { const { text } = event.payload; if (streamingMessageId) { messages.update((msgs) => msgs.map((m) => m.id === streamingMessageId ? { ...m, content: m.content + text } : m ) ); } }) ); // Ergebnis (Kosten, Token, Modell) listeners.push( await listen('claude-result', (event) => { const { cost, tokens, session_id, model } = event.payload; console.log('📊 Ergebnis:', { cost: cost ? `$${cost.toFixed(4)}` : '-', tokens, model }); // Modell an die Streaming-Nachricht anhängen if (model && streamingMessageId) { messages.update((msgs) => msgs.map((m) => m.id === streamingMessageId ? { ...m, model } : m) ); currentModel.set(model); } // Session-Statistiken aktualisieren if (tokens || cost) { sessionStats.update((s) => ({ totalTokensIn: s.totalTokensIn + (tokens?.input || 0), totalTokensOut: s.totalTokensOut + (tokens?.output || 0), totalCost: s.totalCost + (cost || 0), messageCount: s.messageCount + 1, })); } }) ); // STOPP-Signal listeners.push( await listen('agents-stopped', () => { console.log('🛑 STOPP-Signal empfangen'); streamingMessageId = null; clearAll(); }) ); console.log('✅ Event-Listener initialisiert'); } // Listener aufräumen export async function cleanupEventListeners(): Promise { for (const unlisten of listeners) { unlisten(); } listeners = []; } // Agent-Typ mappen function mapAgentType(type: string): 'main' | 'explore' | 'plan' | 'bash' { const typeMap: Record = { main: 'main', 'Main Agent': 'main', Main: 'main', explore: 'explore', Explore: 'explore', plan: 'plan', Plan: 'plan', bash: 'bash', Bash: 'bash' }; return typeMap[type] || 'main'; }