- 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>
200 lines
6.2 KiB
TypeScript
200 lines
6.2 KiB
TypeScript
/**
|
|
* Logging Middleware
|
|
*
|
|
* Logs all events for debugging purposes.
|
|
* Can be enabled/disabled based on environment or flags.
|
|
*/
|
|
|
|
import type { EventMiddleware, ClientEvent } from '../types.js';
|
|
|
|
/**
|
|
* Event categories for colored logging
|
|
*/
|
|
const EVENT_CATEGORIES: Record<string, { color: string; label: string }> = {
|
|
// LLM events
|
|
'llm:thinking': { color: '#3b82f6', label: 'LLM' },
|
|
'llm:chunk': { color: '#3b82f6', label: 'LLM' },
|
|
'llm:response': { color: '#22c55e', label: 'LLM' },
|
|
'llm:tool-call': { color: '#f59e0b', label: 'Tool' },
|
|
'llm:tool-result': { color: '#f59e0b', label: 'Tool' },
|
|
'llm:error': { color: '#ef4444', label: 'Error' },
|
|
'llm:unsupported-input': { color: '#ef4444', label: 'Error' },
|
|
|
|
// Approval events
|
|
'approval:request': { color: '#8b5cf6', label: 'Approval' },
|
|
'approval:response': { color: '#8b5cf6', label: 'Approval' },
|
|
|
|
// Session events
|
|
'session:title-updated': { color: '#06b6d4', label: 'Session' },
|
|
|
|
// Context events
|
|
'context:compacted': { color: '#64748b', label: 'Context' },
|
|
'context:pruned': { color: '#64748b', label: 'Context' },
|
|
|
|
// Queue events
|
|
'message:queued': { color: '#ec4899', label: 'Queue' },
|
|
'message:dequeued': { color: '#ec4899', label: 'Queue' },
|
|
|
|
// Run lifecycle
|
|
'run:complete': { color: '#22c55e', label: 'Run' },
|
|
|
|
// Connection (client-only)
|
|
'connection:status': { color: '#64748b', label: 'Connection' },
|
|
};
|
|
|
|
/**
|
|
* Get summary for an event (for compact logging)
|
|
*/
|
|
function getEventSummary(event: ClientEvent): string {
|
|
switch (event.name) {
|
|
case 'llm:thinking':
|
|
return `session=${event.sessionId}`;
|
|
case 'llm:chunk':
|
|
return `${event.chunkType}: "${event.content.slice(0, 30)}${event.content.length > 30 ? '...' : ''}"`;
|
|
case 'llm:response':
|
|
return `tokens=${event.tokenUsage?.totalTokens ?? '?'}, model=${event.model ?? '?'}`;
|
|
case 'llm:tool-call':
|
|
return `${event.toolName}(${JSON.stringify(event.args).slice(0, 50)}...)`;
|
|
case 'llm:tool-result':
|
|
return `${event.toolName}: ${event.success ? 'success' : 'failed'}`;
|
|
case 'llm:error':
|
|
return event.error?.message ?? 'Unknown error';
|
|
case 'approval:request': {
|
|
// toolName is in metadata for tool_confirmation type
|
|
const toolName =
|
|
'metadata' in event && event.metadata && 'toolName' in event.metadata
|
|
? event.metadata.toolName
|
|
: 'unknown';
|
|
return `${event.type}: ${toolName}`;
|
|
}
|
|
case 'approval:response':
|
|
return `${event.approvalId}: ${event.status}`;
|
|
case 'session:title-updated':
|
|
return `"${event.title}"`;
|
|
case 'run:complete':
|
|
return `reason=${event.finishReason}, steps=${event.stepCount}`;
|
|
case 'message:dequeued':
|
|
return `count=${event.count}`;
|
|
case 'connection:status':
|
|
return event.status;
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Configuration for logging middleware
|
|
*/
|
|
export interface LoggingConfig {
|
|
/** Enable/disable logging */
|
|
enabled: boolean;
|
|
/** Log full event payload (verbose) */
|
|
verbose: boolean;
|
|
/** Event names to exclude from logging */
|
|
exclude: string[];
|
|
/** Only log these event names (if set, overrides exclude) */
|
|
include?: string[];
|
|
}
|
|
|
|
const defaultConfig: LoggingConfig = {
|
|
enabled: process.env.NODE_ENV === 'development',
|
|
verbose: false,
|
|
exclude: ['llm:chunk'], // Chunks are too noisy by default
|
|
};
|
|
|
|
let config: LoggingConfig = { ...defaultConfig };
|
|
|
|
/**
|
|
* Configure the logging middleware
|
|
*/
|
|
export function configureLogging(newConfig: Partial<LoggingConfig>): void {
|
|
config = { ...config, ...newConfig };
|
|
}
|
|
|
|
/**
|
|
* Reset logging config to defaults
|
|
*/
|
|
export function resetLoggingConfig(): void {
|
|
config = { ...defaultConfig };
|
|
}
|
|
|
|
/**
|
|
* Logging middleware
|
|
*
|
|
* Logs events to the console with colored labels and summaries.
|
|
* Disabled by default in production.
|
|
*/
|
|
export const loggingMiddleware: EventMiddleware = (event, next) => {
|
|
// Always pass through
|
|
next(event);
|
|
|
|
// Skip if disabled
|
|
if (!config.enabled) {
|
|
return;
|
|
}
|
|
|
|
// Check include/exclude filters
|
|
if (config.include && !config.include.includes(event.name)) {
|
|
return;
|
|
}
|
|
if (!config.include && config.exclude.includes(event.name)) {
|
|
return;
|
|
}
|
|
|
|
// Get category info
|
|
const category = EVENT_CATEGORIES[event.name] ?? { color: '#9ca3af', label: 'Event' };
|
|
const summary = getEventSummary(event);
|
|
|
|
// Log with styling
|
|
const sessionId = 'sessionId' in event ? event.sessionId : undefined;
|
|
const sessionSuffix = sessionId ? ` [${sessionId.slice(0, 8)}]` : '';
|
|
|
|
console.log(
|
|
`%c[${category.label}]%c ${event.name}${sessionSuffix}${summary ? ` - ${summary}` : ''}`,
|
|
`color: ${category.color}; font-weight: bold`,
|
|
'color: inherit'
|
|
);
|
|
|
|
// Verbose mode: log full payload
|
|
if (config.verbose) {
|
|
console.log(' Payload:', event);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create a custom logging middleware with specific config
|
|
*/
|
|
export function createLoggingMiddleware(customConfig: Partial<LoggingConfig>): EventMiddleware {
|
|
const localConfig = { ...defaultConfig, ...customConfig };
|
|
|
|
return (event, next) => {
|
|
next(event);
|
|
|
|
if (!localConfig.enabled) {
|
|
return;
|
|
}
|
|
|
|
if (localConfig.include && !localConfig.include.includes(event.name)) {
|
|
return;
|
|
}
|
|
if (!localConfig.include && localConfig.exclude.includes(event.name)) {
|
|
return;
|
|
}
|
|
|
|
const category = EVENT_CATEGORIES[event.name] ?? { color: '#9ca3af', label: 'Event' };
|
|
const summary = getEventSummary(event);
|
|
const sessionId = 'sessionId' in event ? event.sessionId : undefined;
|
|
const sessionSuffix = sessionId ? ` [${sessionId.slice(0, 8)}]` : '';
|
|
|
|
console.log(
|
|
`%c[${category.label}]%c ${event.name}${sessionSuffix}${summary ? ` - ${summary}` : ''}`,
|
|
`color: ${category.color}; font-weight: bold`,
|
|
'color: inherit'
|
|
);
|
|
|
|
if (localConfig.verbose) {
|
|
console.log(' Payload:', event);
|
|
}
|
|
};
|
|
}
|