Hook System

Kollab CLI uses a comprehensive event-driven hook system that allows plugins to intercept and modify behavior at every stage of operation.

Overview

The hook system is built on an event bus architecture where:

  • Events are emitted at key points in the application lifecycle
  • Hooks are async callbacks that respond to specific event types
  • Priority determines the order hooks execute (higher values first)
  • Data flow allows hooks to modify and enrich data as it passes through

Event Types

User Input Events

EventDescription
USER_INPUT_PREBefore processing user input
USER_INPUTDuring user input processing
USER_INPUT_POSTAfter user input has been processed
KEY_PRESS_PREBefore key press is handled
KEY_PRESSDuring key press handling
PASTE_DETECTEDWhen clipboard paste is detected

LLM Events

EventDescription
LLM_REQUEST_PREBefore sending request to LLM API
LLM_REQUESTDuring LLM API call
LLM_RESPONSE_PREBefore processing LLM response
LLM_RESPONSE_POSTAfter LLM response is processed
LLM_THINKINGDuring LLM thinking/reasoning phase
CANCEL_REQUESTWhen LLM request is cancelled

Tool Events

EventDescription
TOOL_CALL_PREBefore executing a tool call
TOOL_CALLDuring tool execution
TOOL_CALL_POSTAfter tool execution completes

System Events

EventDescription
SYSTEM_STARTUPApplication startup
SYSTEM_SHUTDOWNApplication shutdown
RENDER_FRAMEEach render frame update

Slash Command Events

EventDescription
SLASH_COMMAND_DETECTEDWhen a slash command is detected
SLASH_COMMAND_EXECUTEDuring command execution
SLASH_COMMAND_COMPLETEAfter command completes
SLASH_COMMAND_ERRORWhen command execution fails

Hook Priority

Hooks execute in priority order, with higher values executing first:

PriorityValueUse Case
SYSTEM1000Core system operations
SECURITY900Security checks and validation
PREPROCESSING500Data transformation before processing
LLM100LLM-related processing
POSTPROCESSING50Data transformation after processing
DISPLAY10Display and rendering operations

Registering Hooks

Register hooks using the Hook class and event bus:

from core.events import Event, EventType, Hook, HookPriority

async def register_hooks(self):
    """Register plugin hooks."""

    # Create a hook
    hook = Hook(
        name="my_input_processor",
        plugin_name=self.name,
        event_type=EventType.USER_INPUT_PRE,
        priority=HookPriority.PREPROCESSING.value,
        callback=self.process_input,
        timeout=30,           # Max execution time (seconds)
        retry_attempts=3,     # Retry on failure
        error_action="continue"  # or "stop"
    )

    # Register with event bus
    await self.event_bus.register_hook(hook)

Hook Callbacks

Hook callbacks are async functions that receive data and the event object:

async def process_input(self, data: dict, event: Event) -> dict:
    """Process user input.

    Args:
        data: Event-specific data (varies by event type)
        event: The event object with metadata

    Returns:
        Modified data dict to pass to next hook
    """
    message = data.get("message", "")

    # Enrich the data
    enriched = {
        "original_message": message,
        "message": message.strip(),
        "processed_at": event.timestamp,
        "word_count": len(message.split())
    }

    return enriched

The data returned by each hook is passed to the next hook in the chain. Hooks can modify, enrich, or filter data as needed.

Example: Security Hook

Here's a complete example of a security validation hook:

from core.events import EventType, Hook, HookPriority

class SecurityPlugin:
    """Plugin that validates tool calls for security."""

    HIGH_RISK_PATTERNS = ["rm -rf", "sudo", "chmod 777"]

    async def register_hooks(self):
        hook = Hook(
            name="security_tool_validator",
            plugin_name=self.name,
            event_type=EventType.TOOL_CALL_PRE,
            priority=HookPriority.SECURITY.value,
            callback=self.validate_tool_call
        )
        await self.event_bus.register_hook(hook)

    async def validate_tool_call(self, data, event):
        """Validate tool calls before execution."""
        command = data.get("command", "")

        # Check for high-risk patterns
        for pattern in self.HIGH_RISK_PATTERNS:
            if pattern in command.lower():
                return {
                    **data,
                    "blocked": True,
                    "risk_level": "high",
                    "reason": f"Blocked pattern: {pattern}"
                }

        return {**data, "validated": True, "risk_level": "low"}