Files
SuperCharged-Claude-Code-Up…/dexto/packages/webui/lib/events
admin b52318eeae feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing
- Add AUTO-TRIGGER-SUMMARY.md documentation
- Add FINAL-INTEGRATION-SUMMARY.md documentation
- Complete Prometheus integration (6 commands + 4 tools)
- Complete Dexto integration (12 commands + 5 tools)
- Enhanced Ralph with access to all agents
- Fix /clawd command (removed disable-model-invocation)
- Update hooks.json to v5 with intelligent routing
- 291 total skills now available
- All 21 commands with automatic routing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
b52318eeae · 2026-01-28 00:27:56 +04:00
History
..

Event System

Centralized event dispatch system for the Dexto WebUI.

Architecture

SSE Stream → EventBus → Middleware → Handlers → Zustand Stores → React Components

Components

  1. EventBus (EventBus.ts) - Central event dispatcher

    • Manages event subscriptions
    • Executes middleware pipeline
    • Maintains event history for debugging
  2. Handlers (handlers.ts) - Event-to-store mapping

    • Registry of handlers by event name
    • Each handler updates appropriate Zustand stores
    • Simple, focused functions with minimal logic
  3. Middleware (middleware/) - Cross-cutting concerns

    • Logging middleware for debugging
    • Extensible for analytics, notifications, etc.
  4. Types (types.ts) - TypeScript definitions

    • Re-exports StreamingEvent from @dexto/core
    • Client-only events (connection status, etc.)

Usage

Setting Up Event Handlers

In your app initialization or EventBusProvider:

import { useEventBus } from '@/components/providers/EventBusProvider';
import { setupEventHandlers } from '@/lib/events';

function MyApp() {
  const bus = useEventBus();

  useEffect(() => {
    const cleanup = setupEventHandlers(bus);
    return cleanup;
  }, [bus]);

  return <YourComponents />;
}

Dispatching Events

Events are automatically dispatched from the SSE stream. For testing or manual dispatch:

import { eventBus } from '@/lib/events';

eventBus.dispatch({
  name: 'llm:chunk',
  sessionId: 'session-123',
  content: 'Hello',
  chunkType: 'text',
});

Subscribing to Events

For custom logic beyond the default handlers:

import { useEventBus } from '@/components/providers/EventBusProvider';

function MyComponent() {
  const bus = useEventBus();

  useEffect(() => {
    const subscription = bus.on('llm:response', (event) => {
      console.log('Response received:', event.content);
    });

    return () => subscription.unsubscribe();
  }, [bus]);
}

Event Handlers

Each handler corresponds to a StreamingEvent type from @dexto/core:

Event Handler Store Updates
llm:thinking handleLLMThinking chatStore (processing=true), agentStore (status='thinking')
llm:chunk handleLLMChunk chatStore (append to streaming message)
llm:response handleLLMResponse chatStore (finalize message with metadata)
llm:tool-call handleToolCall chatStore (add tool message), agentStore (status='executing_tool')
llm:tool-result handleToolResult chatStore (update tool message with result)
llm:error handleLLMError chatStore (set error, processing=false), agentStore (status='idle')
approval:request handleApprovalRequest agentStore (status='awaiting_approval')
approval:response handleApprovalResponse agentStore (status='idle')
run:complete handleRunComplete chatStore (processing=false), agentStore (status='idle')
session:title-updated handleSessionTitleUpdated (handled by TanStack Query)
message:dequeued handleMessageDequeued chatStore (add user message from queue)
context:compressed handleContextCompressed (log for debugging)

Adding New Handlers

  1. Define the handler function in handlers.ts:
function handleMyNewEvent(event: EventByName<'my:event'>): void {
  const { sessionId, data } = event;
  // Update stores as needed
  useSomeStore.getState().updateSomething(sessionId, data);
}
  1. Register in registerHandlers():
export function registerHandlers(): void {
  // ... existing handlers
  handlers.set('my:event', handleMyNewEvent);
}
  1. Add tests in handlers.test.ts:
describe('handleMyNewEvent', () => {
  it('should update the store correctly', () => {
    const event = {
      name: 'my:event' as const,
      sessionId: TEST_SESSION_ID,
      data: 'test',
    };

    handleMyNewEvent(event);

    const state = useSomeStore.getState();
    expect(state.data).toBe('test');
  });
});

Testing

Run tests:

pnpm test:unit packages/webui/lib/events/handlers.test.ts

Each handler is tested in isolation to verify correct store updates.

Design Principles

  1. Handler simplicity - Handlers extract data from events and call store actions. No complex logic.

  2. Store-driven - All state changes go through Zustand stores. Handlers don't mutate state directly.

  3. Type safety - Events are strongly typed via StreamingEvent union from @dexto/core.

  4. Testability - Each handler can be tested independently with mocked stores.

  5. Single responsibility - One handler per event type, focused on one concern.

Migration from useChat

The handler registry replaces the 200+ LOC switch statement in useChat.ts:

Before:

// In useChat.ts
switch (event.name) {
  case 'llm:thinking':
    setProcessing(true);
    // ... more logic
    break;
  case 'llm:chunk':
    // ... 30+ lines
    break;
  // ... 10+ more cases
}

After:

// In handlers.ts
function handleLLMThinking(event) {
  useChatStore.getState().setProcessing(event.sessionId, true);
  useAgentStore.getState().setThinking(event.sessionId);
}

function handleLLMChunk(event) {
  // Simple, focused logic
}

// Register all handlers
registerHandlers();

This provides:

  • Better testability (test each handler independently)
  • Clearer separation of concerns
  • Easier to add/modify handlers
  • Type safety with EventByName helper