Files
SuperCharged-Claude-Code-Up…/dexto/packages/webui/lib/events/README.md
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>
2026-01-28 00:27:56 +04:00

209 lines
5.4 KiB
Markdown

# 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:
```typescript
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:
```typescript
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:
```typescript
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`:
```typescript
function handleMyNewEvent(event: EventByName<'my:event'>): void {
const { sessionId, data } = event;
// Update stores as needed
useSomeStore.getState().updateSomething(sessionId, data);
}
```
2. Register in `registerHandlers()`:
```typescript
export function registerHandlers(): void {
// ... existing handlers
handlers.set('my:event', handleMyNewEvent);
}
```
3. Add tests in `handlers.test.ts`:
```typescript
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:
```bash
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:**
```typescript
// In useChat.ts
switch (event.name) {
case 'llm:thinking':
setProcessing(true);
// ... more logic
break;
case 'llm:chunk':
// ... 30+ lines
break;
// ... 10+ more cases
}
```
**After:**
```typescript
// 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