src — middleware

Module: src-middleware Cohesion: 0.80 Members: 0

src — middleware

The src/middleware module provides a robust and extensible system for controlling and enhancing conversation flow within Code-Buddy. Inspired by elegant middleware architectures, it allows for the injection of custom logic at key points in the conversation lifecycle, enabling features like turn limits, cost management, context auto-compaction, and more.

This module is designed for developers who need to understand how conversation constraints and automated actions are applied, or who wish to extend Code-Buddy's capabilities with new middleware.

Overview

The middleware system operates on a "pipeline" model. As a conversation turn begins and ends, a series of registered ConversationMiddleware instances are executed in a defined order. Each middleware can inspect the current ConversationContext and return a MiddlewareResult that dictates whether the conversation should CONTINUE, STOP, COMPACT the context, or INJECT_MESSAGE into the chat.

This architecture provides a clean separation of concerns, allowing individual policies (e.g., turn limits, cost limits) to be implemented as distinct, pluggable components.

Core Concepts

ConversationMiddleware Interface

The heart of the system is the ConversationMiddleware interface, defined in src/middleware/types.ts. Any class implementing this interface can be added to the pipeline.

export interface ConversationMiddleware {
  readonly name: string;
  readonly priority: number; class="hl-cmt">// Lower priority runs first

  beforeTurn(context: ConversationContext): Promise<MiddlewareResult>;
  afterTurn(context: ConversationContext): Promise<MiddlewareResult>;
  reset(): void;
}

ConversationContext

The ConversationContext (defined in src/middleware/types.ts) is a comprehensive object passed to each middleware. It provides all necessary information about the current state of the conversation:

MiddlewareResult and MiddlewareAction

After executing its logic, a middleware returns a MiddlewareResult object. The action property of this result dictates the next step for the pipeline:

Helper functions like continueResult(), stopResult(), compactResult(), and injectMessageResult() are provided in src/middleware/types.ts for convenience.

Middleware Implementations (src/middleware/middlewares.ts)

This file contains several concrete implementations of ConversationMiddleware, each addressing a specific aspect of conversation control:

Factory Functions

src/middleware/middlewares.ts also provides factory functions to easily create common middleware stacks:

Middleware Pipeline (src/middleware/pipeline.ts)

The MiddlewarePipeline class is responsible for managing and executing the registered middlewares.

MiddlewarePipeline

Pipeline Execution Flow (runBefore example)

sequenceDiagram
    participant App as Application
    participant P as MiddlewarePipeline
    participant M1 as Middleware A (Prio 10)
    participant M2 as Middleware B (Prio 20)
    participant M3 as Middleware C (Prio 30)

    App->>P: runBefore(context)
    P->>P: Emit 'middleware:before' (M1)
    P->>M1: beforeTurn(context)
    M1-->>P: continueResult()
    P->>P: Emit 'middleware:before' (M2)
    P->>M2: beforeTurn(context)
    M2-->>P: injectMessageResult("Warning!")
    P->>P: Emit 'middleware:action' (M2, INJECT_MESSAGE)
    P-->>App: injectMessageResult("Warning!")
    Note right of App: Pipeline stops, action handled by App

PipelineBuilder

The PipelineBuilder class provides a fluent API for constructing MiddlewarePipeline instances.

The createPipeline() function is a convenient entry point to start building a pipeline.

Integration with the Codebase

The middleware system is primarily consumed by the core conversation agent (e.g., src/agent/codebuddy-agent.ts).

  1. Initialization: An instance of MiddlewarePipeline is created, often using PipelineBuilder and factory functions like createDefaultMiddlewares().

  1. Turn Lifecycle:

  1. Action Handling: The agent is responsible for interpreting the MiddlewareResult returned by runBefore or runAfter and taking the appropriate action (e.g., stopping the conversation, triggering compaction, displaying an injected message).
  2. Reset: When a conversation is cleared, the agent calls pipeline.reset() to clear the state of all middlewares.
  3. Tool Calls: The ToolExecutionLimitMiddleware.checkToolCall() method is designed to be called by the tool execution logic to enforce per-turn tool limits.

Extending the Module

To add new conversation control logic:

  1. Create a new class that implements the ConversationMiddleware interface.
  2. Define name and priority. Choose a priority that makes sense relative to existing middlewares (e.g., rate limiting might be very low, hard limits like turn/price might be low, warnings higher).
  3. Implement beforeTurn(context) and/or afterTurn(context). Use the context object to access conversation state and return an appropriate MiddlewareResult.
  4. Implement reset() to clear any internal state.
  5. Add your middleware to a MiddlewarePipeline using pipeline.add() or pipelineBuilder.use(). Consider adding it to createDefaultMiddlewares() or createYoloMiddlewares() if it's a core feature.