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>
This commit is contained in:
admin
2026-01-28 00:27:56 +04:00
Unverified
parent 3b128ba3bd
commit b52318eeae
1724 changed files with 351216 additions and 0 deletions

View File

@@ -0,0 +1,353 @@
# EventBus Usage Guide
Complete integration guide for the EventBus system with SSE event dispatching.
## Architecture
```
SSE Stream → useEventDispatch → EventBus → Middleware → Handlers → Stores → UI Updates
```
## Setup
### 1. Wrap App with EventBusProvider
```tsx
// app/root.tsx or similar
import { EventBusProvider } from '@/components/providers/EventBusProvider';
export default function App() {
return (
<EventBusProvider
enableLogging={true} // Console logging (dev only by default)
enableActivityLogging={true} // Event log store
enableNotifications={true} // Toast notifications
>
<YourApp />
</EventBusProvider>
);
}
```
The provider automatically:
- Registers middleware (logging, activity, notifications)
- Sets up event handlers to dispatch to stores
- Cleans up on unmount
### 2. Dispatch SSE Events
In your message streaming component:
```tsx
import { createMessageStream } from '@dexto/client-sdk';
import { useEventDispatch } from '@/lib/events';
import { client } from '@/lib/client';
export function useMessageStream(sessionId: string) {
const { dispatchEvent } = useEventDispatch();
const sendMessage = async (message: string) => {
const responsePromise = client.api['message-stream'].$post({
json: { message, sessionId }
});
const stream = createMessageStream(responsePromise);
for await (const event of stream) {
// Dispatch to EventBus - handlers will update stores
dispatchEvent(event);
}
};
return { sendMessage };
}
```
### 3. Components React to Store Updates
Components subscribe to stores as usual:
```tsx
import { useChatStore } from '@/lib/stores/chatStore';
import { useAgentStore } from '@/lib/stores/agentStore';
export function ChatInterface({ sessionId }: { sessionId: string }) {
// Get session state
const sessionState = useChatStore(state =>
state.getSessionState(sessionId)
);
// Get agent status
const agentStatus = useAgentStore(state => state.status);
return (
<div>
{sessionState.streamingMessage && (
<StreamingMessage message={sessionState.streamingMessage} />
)}
{sessionState.messages.map(msg => (
<Message key={msg.id} message={msg} />
))}
{agentStatus === 'thinking' && <ThinkingIndicator />}
</div>
);
}
```
## Event Flow Examples
### Example 1: LLM Response
```
1. SSE: llm:thinking
→ Handler: handleLLMThinking
→ Store: agentStore.setThinking(sessionId)
→ Store: chatStore.setProcessing(sessionId, true)
→ UI: Show thinking indicator
2. SSE: llm:chunk (content: "Hello")
→ Handler: handleLLMChunk
→ Store: chatStore.setStreamingMessage(sessionId, newMessage)
→ UI: Show streaming message
3. SSE: llm:chunk (content: " world")
→ Handler: handleLLMChunk
→ Store: chatStore.appendToStreamingMessage(sessionId, " world")
→ UI: Update streaming message
4. SSE: llm:response (tokenUsage, model)
→ Handler: handleLLMResponse
→ Store: chatStore.finalizeStreamingMessage(sessionId, metadata)
→ UI: Move to messages array, show token count
5. SSE: run:complete
→ Handler: handleRunComplete
→ Store: agentStore.setIdle()
→ Store: chatStore.setProcessing(sessionId, false)
→ UI: Hide thinking indicator
```
### Example 2: Tool Call
```
1. SSE: llm:tool-call (toolName: "read_file", callId: "123")
→ Handler: handleToolCall
→ Store: chatStore.addMessage(sessionId, toolMessage)
→ Store: agentStore.setExecutingTool(sessionId, "read_file")
→ UI: Show tool message, show executing indicator
2. SSE: llm:tool-result (callId: "123", success: true, sanitized: "File contents")
→ Handler: handleToolResult
→ Store: chatStore.updateMessage(sessionId, messageId, { toolResult, success })
→ UI: Update tool message with result
```
### Example 3: Approval Request
```
1. SSE: approval:request (type: TOOL_CONFIRMATION, toolName: "write_file")
→ Handler: handleApprovalRequest
→ Store: agentStore.setAwaitingApproval(sessionId)
→ Middleware (notification): Show toast "Tool write_file needs approval"
→ UI: Show approval dialog
2. User clicks "Approve"
→ API: POST /api/approval/respond { status: "approved" }
3. SSE: approval:response (status: "approved")
→ Handler: handleApprovalResponse
→ Store: agentStore.setIdle()
→ UI: Hide approval dialog, resume processing
```
## Middleware
### Logging Middleware
Logs all events to console (dev mode only by default):
```
[EventBus] llm:thinking → sessionId: abc-123
[EventBus] llm:chunk → sessionId: abc-123, chunkType: text, content: Hello
```
### Activity Middleware
Logs events to EventLogStore for debugging panel:
```tsx
import { useEventLogStore } from '@/lib/stores/eventLogStore';
export function DebugPanel() {
const events = useEventLogStore(state => state.events);
return (
<div>
{events.map(event => (
<div key={event.id}>
{event.timestamp} - {event.category} - {event.description}
</div>
))}
</div>
);
}
```
### Notification Middleware
Shows toast notifications for important events:
- `approval:request` → "Tool X needs your approval"
- `llm:error` → Error message with recovery info
- Session-aware: Only notifies for current session
## Testing
### Unit Test - Individual Handler
```typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { ClientEventBus } from './EventBus';
import { handleLLMChunk } from './handlers';
import { useChatStore } from '../stores/chatStore';
describe('handleLLMChunk', () => {
beforeEach(() => {
useChatStore.setState({ sessions: new Map() });
});
it('should create streaming message on first chunk', () => {
handleLLMChunk({
name: 'llm:chunk',
sessionId: 'test',
content: 'Hello',
chunkType: 'text',
});
const state = useChatStore.getState().getSessionState('test');
expect(state.streamingMessage?.content).toBe('Hello');
});
});
```
### Integration Test - Full Flow
```typescript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { ClientEventBus } from './EventBus';
import { setupEventHandlers } from './handlers';
import { useChatStore } from '../stores/chatStore';
import { useAgentStore } from '../stores/agentStore';
describe('EventBus Integration', () => {
let bus: ClientEventBus;
let cleanup: () => void;
beforeEach(() => {
bus = new ClientEventBus();
cleanup = setupEventHandlers(bus);
// Reset stores
useChatStore.setState({ sessions: new Map() });
useAgentStore.setState({ status: 'idle', /* ... */ });
});
afterEach(() => {
cleanup();
});
it('should handle full LLM response flow', () => {
// Thinking
bus.dispatch({ name: 'llm:thinking', sessionId: 'test' });
expect(useAgentStore.getState().status).toBe('thinking');
// Chunk
bus.dispatch({
name: 'llm:chunk',
sessionId: 'test',
content: 'Response',
chunkType: 'text',
});
expect(useChatStore.getState().getSessionState('test').streamingMessage)
.not.toBeNull();
// Complete
bus.dispatch({
name: 'llm:response',
sessionId: 'test',
tokenUsage: { totalTokens: 100 },
});
expect(useChatStore.getState().getSessionState('test').messages)
.toHaveLength(1);
});
});
```
## Custom Middleware
Create custom middleware to extend functionality:
```typescript
import type { EventMiddleware } from '@/lib/events';
export const analyticsMiddleware: EventMiddleware = (event, next) => {
// Track analytics
if (event.name === 'llm:response') {
analytics.track('llm_response', {
sessionId: event.sessionId,
model: event.model,
tokens: event.tokenUsage?.totalTokens,
});
}
return next(event);
};
// Use it
<EventBusProvider middleware={[analyticsMiddleware]}>
<App />
</EventBusProvider>
```
## Advanced: Direct Bus Access
For cases where you need direct access to the bus:
```tsx
import { useEventBus } from '@/components/providers/EventBusProvider';
export function CustomComponent() {
const bus = useEventBus();
useEffect(() => {
// Subscribe to specific event
const sub = bus.on('llm:error', (event) => {
console.error('LLM Error:', event.error);
});
return () => sub.unsubscribe();
}, [bus]);
// Dispatch custom event
const handleAction = () => {
bus.dispatch({
name: 'custom:event',
sessionId: 'test',
// ... custom data
});
};
}
```
## Summary
1. **EventBusProvider** - Wrap your app, registers middleware and handlers
2. **useEventDispatch** - Use in components to dispatch SSE events
3. **Middleware** - Intercepts events (logging, activity, notifications)
4. **Handlers** - Process events and update stores
5. **Stores** - Hold state, trigger React re-renders
6. **Components** - Subscribe to stores, render UI
All wired together automatically. Just dispatch events and let the system handle the rest.