diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100644 new mode 100755 index a8f390e..272db0e --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.0] - 2025-03-03 + +### Added +- **Delegation System** - Complete busy-state handling system + - `RequestClassifier` - Fast request analysis (<50ms) + - `AgentPoolManager` - Auto-scaling agent pool with 8 agent types + - `DelegationEngine` - 4 delegation strategies (full, parallel, hierarchical, hybrid) + - `ProgressStreamer` - Real-time progress updates with SSE/WebSocket support + - `ContextHandoffManager` - Inter-agent context transfer protocol + - `QualityGate` - Confidence thresholds and auto-escalation + - `IntegrationAdapters` - Native support for OpenClaw, Claude Code CLI, Cursor, Aider, Copilot + +- **3rd Party Integration Support** + - OpenClaw adapter with parallel execution (4 projects × 3 roles) + - Claude Code CLI adapter with tool registration + - Generic adapter for custom integrations + - Standardized `IntegrationAdapter` interface + +- **Agent Types** + - `fast-responder` - Quick answers (< 2s) + - `explorer` - Code navigation and file search + - `researcher` - Deep analysis and documentation + - `coder` - Code generation and implementation + - `reviewer` - Code review and quality checks + - `planner` - Architecture and planning + - `executor` - Command execution and file operations + - `analyzer` - Debugging and profiling + +- **Test Suite** + - Comprehensive test file for delegation system + - 6 test cases covering all components + - All tests passing + +### Changed +- Updated README with delegation system documentation +- Updated architecture diagrams +- Added new examples for delegation patterns + ## [1.1.0] - 2025-03-03 ### Added @@ -76,16 +114,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Release Notes +### v1.2.0 - Delegation System Release + +This release adds a complete delegation system for handling busy-state scenarios: + +**Key Features:** +- Automatic request classification and routing +- Agent pool with auto-scaling (8 agent types) +- 4 delegation strategies for different use cases +- Real-time progress streaming +- Quality gates with confidence thresholds +- Native 3rd party tool integration + +**Use Cases:** +- Handle high-traffic AI services +- Delegate requests when main agent is busy +- Parallel multi-agent execution +- Progressive result streaming + ### v1.1.0 - Integration Release This release adds full integration support for both Claude Code and OpenClaw, making it easy to incorporate context compaction and deterministic pipeline orchestration into existing AI development workflows. -**Key Highlights:** -- One-line integration with Claude Code CLI -- OpenClaw-compatible pipeline definitions -- Support for the 4×3 parallel agent pattern (4 projects × 3 roles) -- Persistent memory across sessions - ### v1.0.0 - Initial Release Initial release of the Agentic Compaction & Pipeline System, providing: diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 7fed772..5735bc2 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ InstallationQuick StartIntegrations • + Delegation SystemAPI Reference

@@ -41,16 +42,17 @@ and even pushing everything to this Git repository. ## Overview -This project provides two complementary systems: +This project provides three complementary systems: 1. **Agent System** - Context compaction, token management, and agent orchestration 2. **Pipeline System** - Deterministic state machine, parallel execution, and event-driven coordination +3. **Delegation System** - Intelligent request routing, agent pool management, and busy-state handling Built based on the architectural principles described in [How I Built a Deterministic Multi-Agent Dev Pipeline Inside OpenClaw](https://dev.to/ggondim/how-i-built-a-deterministic-multi-agent-dev-pipeline-inside-openclaw-and-contributed-a-missing-4ool). --- -## Features +## ✨ Features ### Agent System @@ -70,67 +72,92 @@ Built based on the architectural principles described in [How I Built a Determin - ✅ **Event-Driven Coordination** - Pub/sub event bus with automatic trigger chains - ✅ **Workspace Isolation** - Per-agent tools, memory, identity, permissions - ✅ **YAML Workflow Parser** - Lobster-compatible workflow definitions -- ✅ **Claude Code Integration** - Ready-to-use integration layer + +### 🆕 Delegation System (NEW!) + +- ✅ **Request Classification** - Fast analysis (<50ms) for optimal routing +- ✅ **Agent Pool Management** - Auto-scaling pool with 8 agent types +- ✅ **Delegation Engine** - 4 strategies: full, parallel, hierarchical, hybrid +- ✅ **Progress Streaming** - Real-time updates with SSE/WebSocket support +- ✅ **Context Handoff Protocol** - Seamless inter-agent context transfer +- ✅ **Quality Gate** - Confidence thresholds and auto-escalation +- ✅ **3rd Party Adapters** - Native support for OpenClaw, Claude Code CLI, Cursor, Aider --- ## Architecture ``` -┌─────────────────────────────────────────────────────────────────┐ -│ AGENTIC PIPELINE SYSTEM │ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌───────────────────────┐ ┌───────────────────────┐ │ -│ │ AGENT SYSTEM │ │ PIPELINE SYSTEM │ │ -│ ├───────────────────────┤ ├───────────────────────┤ │ -│ │ • Token Counter │ │ • State Machine │ │ -│ │ • Context Manager │ │ • Parallel Executor │ │ -│ │ • Summarizer │ │ • Event Bus │ │ -│ │ • Orchestrator │ │ • Workspace Manager │ │ -│ │ • Subagent Spawner │ │ • YAML Workflows │ │ -│ │ • Memory Store │ │ • Claude Integration │ │ -│ │ ───────────────────── │ └───────────────────────┘ │ -│ │ INTEGRATIONS: │ │ -│ │ • Claude Code ✅ │ │ -│ │ • OpenClaw ✅ │ │ -│ └───────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────────────────────────────┐ │ -│ │ INTEGRATION LAYER │ │ -│ │ Claude Code │ OpenClaw │ Lobster │ Custom Applications │ │ -│ └─────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────────────────────────┐ +│ AGENTIC PIPELINE SYSTEM │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ AGENT SYSTEM │ │ PIPELINE SYSTEM │ │DELEGATION SYSTEM│ │ +│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │ +│ │ • Token Counter │ │ • State Machine │ │ • Classifier │ │ +│ │ • Context Mgr │ │ • Parallel Exec │ │ • Pool Manager │ │ +│ │ • Summarizer │ │ • Event Bus │ │ • Deleg. Engine │ │ +│ │ • Orchestrator │ │ • Workspace │ │ • Progress Str. │ │ +│ │ • Subagent Sp. │ │ • YAML Workflow │ │ • Quality Gate │ │ +│ │ • Memory Store │ │ • Integration │ │ • Handoff Proto │ │ +│ │ ─────────────── │ └─────────────────┘ └─────────────────┘ │ +│ │ INTEGRATIONS: │ │ +│ │ • Claude Code ✅ │ │ +│ │ • OpenClaw ✅ │ │ +│ └─────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ INTEGRATION LAYER │ │ +│ │ Claude Code │ OpenClaw │ Cursor │ Aider │ Copilot │ Custom │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ ``` -### Data Flow +### Delegation Flow (Busy-State Handling) ``` -User Request → Pipeline Orchestrator → State Machine - │ - ▼ - Parallel Executor (Worker Pool) - │ - ┌─────────────────┼─────────────────┐ - ▼ ▼ ▼ - Agent 1 Agent 2 Agent N - │ │ │ - ▼ ▼ ▼ - Workspace Workspace Workspace - │ │ │ - └─────────────────┼─────────────────┘ - ▼ - Event Bus - │ - ▼ - Context Manager - │ - ▼ - Summarizer (if needed) - │ - ▼ - Response/Next State +User Request → Request Classifier (<50ms) + │ + ▼ + ┌───────────────┐ + │ Classification │ + │ • Complexity │ + │ • Agent Type │ + │ • Priority │ + └───────────────┘ + │ + ▼ + ┌───────────────┐ + │ Pool Manager │ + │ • Find Agent │ + │ • Scale Pool │ + └───────────────┘ + │ + ┌───────────┴───────────┐ + ▼ ▼ +┌───────────────┐ ┌───────────────┐ +│ Delegate │ │ Queue/Wait │ +│ to Agent │ │ (if busy) │ +└───────────────┘ └───────────────┘ + │ │ + ▼ ▼ +┌───────────────┐ ┌───────────────┐ +│ Progress │ │ Escalate │ +│ Streaming │ │ to Main │ +└───────────────┘ └───────────────┘ + │ │ + └───────────┬───────────┘ + ▼ + ┌───────────────┐ + │ Quality Gate │ + │ • Validate │ + │ • Confidence │ + └───────────────┘ + │ + ▼ + Response ``` --- @@ -152,8 +179,8 @@ cd Agentic-Compaction-and-Pipleline-by-GLM-5 # Install dependencies bun install -# Build (if needed) -bun run build +# Run tests +bun run delegation-system/test.ts ``` ### Using Zip Packages @@ -164,19 +191,47 @@ Download the appropriate package from the `downloads/` directory: |---------|-------------|----------| | `agent-system.zip` | Context compaction & orchestration | Building custom AI agents | | `pipeline-system.zip` | Deterministic pipelines | Multi-agent workflows | +| `delegation-system.zip` | Busy-state handling | High-traffic AI services | | `complete-agent-pipeline-system.zip` | Full system | Complete integration | --- ## Quick Start +### Delegation System (Busy-State Handling) + +```typescript +import { DelegationSystem } from './delegation-system'; + +// Initialize the system +const system = new DelegationSystem(); +await system.initialize(); + +// Process a request (auto-delegates when busy) +const response = await system.process({ + id: 'req-1', + content: 'Review this code for security issues', + type: 'review', + streamProgress: true +}); + +console.log(response.result); +console.log(`Processed by: ${response.agentsUsed.join(', ')}`); +console.log(`Confidence: ${response.confidence}`); + +// Subscribe to progress updates +system.onProgress('req-1', (event) => { + console.log(`[${event.type}] ${event.message}`); +}); +``` + ### Agent System ```typescript import { ContextManager, TokenCounter, Summarizer } from './agent-system'; // Initialize components -const tokenCounter = new TokenCounter(128000); // 128k token budget +const tokenCounter = new TokenCounter(128000); const summarizer = new Summarizer(); const contextManager = new ContextManager(tokenCounter, summarizer, { maxTokens: 100000, @@ -184,413 +239,281 @@ const contextManager = new ContextManager(tokenCounter, summarizer, { reserveTokens: 20000 }); -// Add messages +// Add messages with auto-compaction contextManager.addMessage({ role: 'user', content: 'Hello!' }); -contextManager.addMessage({ role: 'assistant', content: 'Hi there!' }); -// Check if compaction needed if (contextManager.needsCompaction()) { const result = await contextManager.compact(); - console.log(`Compacted: ${result.messagesRemoved} messages removed`); + console.log(`Saved ${result.tokensSaved} tokens`); } ``` ### Pipeline System ```typescript -import { DeterministicStateMachine, ParallelExecutionEngine, EventBus } from './pipeline-system'; +import { DeterministicStateMachine, ParallelExecutionEngine } from './pipeline-system'; -// Define workflow -const workflow = ` -name: code-pipeline -states: - - name: analyze - transitions: - - to: implement - event: analyzed - - name: implement - transitions: - - to: test - event: implemented - - name: test - transitions: - - to: complete - event: passed -`; - -// Create pipeline -const eventBus = new EventBus(); -const stateMachine = new DeterministicStateMachine(workflow); -const executor = new ParallelExecutionEngine({ maxConcurrency: 4 }); - -// Run pipeline -await stateMachine.start(); +const executor = new ParallelExecutionEngine({ maxConcurrency: 12 }); +const results = await executor.executeAll([ + { id: 1, type: 'analyze', data: { target: 'module-a' } }, + { id: 2, type: 'analyze', data: { target: 'module-b' } } +]); ``` --- ## Integrations -### Claude Code Integration +### 3rd Party AI Coding Tools Integration -Full integration with Claude Code CLI and IDE extensions: +The delegation system provides **native integration** for popular AI coding tools: + +#### OpenClaw Integration ```typescript -import { ClaudeCodeIntegration } from './agent-system/integrations/claude-code'; +import { DelegationSystem } from './delegation-system'; -// Initialize with Claude Code defaults -const claude = new ClaudeCodeIntegration({ - maxContextTokens: 200000, // Claude's context window - reserveTokens: 40000, // Reserve for response - compactionStrategy: 'hybrid', - autoCompact: true, - compactionThreshold: 0.8, - enableSubagents: true, - maxSubagents: 6, - persistentMemory: true +const system = new DelegationSystem(); +await system.initialize(); + +// Get OpenClaw adapter +const openclaw = system.getAdapter('openclaw'); + +// Configure for OpenClaw pattern (4 projects × 3 roles) +await openclaw.initialize({ + pool: { maxParallelAgents: 12 }, + delegation: { autoDelegate: true } }); -// Add messages with automatic compaction -claude.addMessage({ role: 'user', content: 'Analyze this codebase...' }); - -// Get context for Claude API -const { messages, systemPrompt } = claude.getContextForAPI(); - -// Spawn subagents for complex tasks -const result = await claude.spawnSubagent({ - type: 'researcher', - prompt: 'Research authentication patterns', - priority: 'high' +// Process with OpenClaw-compatible workflow +const response = await system.process({ + content: 'Analyze security across all modules', + metadata: { integration: 'openclaw' } }); - -// Parallel subagent execution (4 projects × 3 roles pattern) -const results = await claude.executeParallelSubagents([ - { type: 'explorer', prompt: 'Find security issues in frontend' }, - { type: 'explorer', prompt: 'Find security issues in backend' }, - { type: 'reviewer', prompt: 'Review API endpoints' } -]); - -// Memory management -await claude.remember('userPreference', { theme: 'dark' }); -const pref = await claude.recall('userPreference'); - -// Save/restore context -await claude.saveContext('milestone-1'); -await claude.loadContext('milestone-1'); - -// Monitor session -const stats = claude.getTokenStats(); -console.log(`Using ${stats.percentage}% of context (${stats.used}/${stats.total} tokens)`); ``` -### OpenClaw Integration - -Native integration with OpenClaw's deterministic multi-agent architecture: +#### Claude Code CLI Integration ```typescript -import { OpenClawIntegration } from './agent-system/integrations/openclaw'; +import { DelegationSystem } from './delegation-system'; -// Initialize with OpenClaw-compatible config -const openclaw = new OpenClawIntegration({ - maxContextTokens: 200000, - compactionStrategy: 'hybrid', - workspaceIsolation: true, - enableLobsterWorkflows: true, - enableParallelExecution: true, - maxParallelAgents: 12, // 4 projects × 3 roles - hooks: { - onCompactionStart: (ctx) => console.log('Compacting...'), - onCompactionEnd: (result) => console.log(`Saved ${result.tokensSaved} tokens`), - onStateTransition: (from, to, ctx) => console.log(`${from} → ${to}`) - } -}); +const system = new DelegationSystem(); +await system.initialize(); -// Add messages with OpenClaw context -openclaw.addMessage({ - role: 'user', +const claudeCode = system.getAdapter('claude-code'); + +// Get Claude Code compatible tools +const tools = claudeCode.getTools(); +// [ +// { name: 'delegate_task', ... }, +// { name: 'check_pool_status', ... } +// ] + +// Process with context compaction +const response = await system.process({ content: 'Implement user authentication', - tags: ['feature', 'auth'], - references: { - files: ['src/auth.ts', 'src/middleware.ts'] - } -}); - -// Spawn agents for parallel execution -const agents = await openclaw.executeParallelAgents([ - { type: 'planner', prompt: 'Plan auth architecture' }, - { type: 'researcher', prompt: 'Research JWT best practices' }, - { type: 'explorer', prompt: 'Find existing auth patterns' } -]); - -// Create deterministic pipeline -const pipeline = openclaw.createPipeline({ - name: 'feature-development', - description: 'Complete feature development workflow', - states: [ - { - name: 'analyze', - type: 'parallel', - agents: ['explorer', 'researcher'], - transitions: [ - { target: 'design', event: 'analysis_complete' } - ] - }, - { - name: 'design', - type: 'sequential', - agents: ['planner'], - transitions: [ - { target: 'implement', event: 'design_approved' } - ] - }, - { - name: 'implement', - type: 'parallel', - agents: ['coder'], - transitions: [ - { target: 'review', event: 'implementation_complete' } - ] - }, - { - name: 'review', - type: 'sequential', - agents: ['reviewer'], - transitions: [ - { target: 'complete', event: 'approved' }, - { target: 'implement', event: 'rejected' } - ] - }, - { - name: 'complete', - type: 'sequential', - transitions: [] - } - ] -}); - -// Execute pipeline -await openclaw.startPipeline(pipeline.id); -await openclaw.transitionPipeline(pipeline.id, 'analysis_complete'); -await openclaw.transitionPipeline(pipeline.id, 'design_approved'); -// ... continue transitions - -// Create isolated workspaces -const workspace = await openclaw.createWorkspace({ - permissions: ['read', 'write'], - quota: { maxFiles: 1000, maxSize: 100 * 1024 * 1024 } + metadata: { integration: 'claude-code' } }); ``` -### Custom Integration - -Build your own integration: +#### Custom Integration ```typescript -import { - ContextManager, - TokenCounter, - Summarizer, - EventBus, - DeterministicStateMachine, - ParallelExecutionEngine -} from './agent-system'; +import { DelegationSystem } from './delegation-system'; -class CustomAISystem { - private contextManager: ContextManager; - private eventBus: EventBus; - private executor: ParallelExecutionEngine; - - constructor(config: any) { - const tokenCounter = new TokenCounter(config.maxTokens); - const summarizer = new Summarizer(); - - this.contextManager = new ContextManager( - tokenCounter, - summarizer, - config.compaction - ); - - this.eventBus = new EventBus(); - this.executor = new ParallelExecutionEngine(config.parallel); - - this.setupEventHandlers(); - } - - private setupEventHandlers() { - this.eventBus.subscribe('context:full', async () => { - await this.contextManager.compact(); - }); - } - - async process(input: string) { - this.contextManager.addMessage({ - role: 'user', - content: input - }); - - // Your custom processing logic - } -} +const system = new DelegationSystem(); + +// Register custom adapter +await system.registerIntegration('my-tool', { + delegation: { enabled: true }, + pool: { maxSize: 20 } +}); + +// Use the custom integration +const response = await system.process({ + content: 'Process this request', + metadata: { integration: 'my-tool' } +}); ``` --- +## Delegation System + +### Agent Types + +| Type | Purpose | Timeout | Concurrency | +|------|---------|---------|-------------| +| `fast-responder` | Quick answers, status checks | 5s | 10 | +| `explorer` | Code navigation, file search | 30s | 4 | +| `researcher` | Deep analysis, documentation | 60s | 3 | +| `coder` | Code generation, implementation | 45s | 4 | +| `reviewer` | Code review, quality checks | 30s | 3 | +| `planner` | Architecture, planning | 60s | 2 | +| `executor` | Command execution, file ops | 30s | 3 | +| `analyzer` | Debugging, profiling | 45s | 3 | + +### Delegation Strategies + +| Strategy | Description | When Used | +|----------|-------------|-----------| +| `full` | Complete delegation to single agent | Quick tasks, simple requests | +| `parallel` | Multiple agents work together | Multi-file, high complexity | +| `hierarchical` | Main agent + subagent collaboration | Complex, needs oversight | +| `hybrid` | Combination based on context | Streaming, progressive results | + +### Request Classification + +Requests are classified into 4 complexity levels: + +| Level | Response Time | Example | +|-------|--------------|---------| +| `quick` | < 2s | "What is TypeScript?" | +| `moderate` | 2-15s | "Review this function for bugs" | +| `streaming` | 5-60s | "Analyze the entire codebase" | +| `complex` | 15-120s | "Implement a new authentication system" | + +--- + ## API Reference -### Agent System API +### Delegation System API -| Class | Method | Description | -|-------|--------|-------------| -| `TokenCounter` | `countTokens(text)` | Estimate token count | -| | `getRemainingBudget()` | Get remaining tokens | -| | `addUsage(count)` | Track token usage | -| `ContextManager` | `addMessage(message)` | Add message to context | -| | `needsCompaction()` | Check if compaction needed | -| | `compact()` | Perform context compaction | -| | `getActiveContext()` | Get current context | -| `Summarizer` | `summarize(messages, options)` | Generate summary | -| `Orchestrator` | `registerAgent(type, config)` | Register agent | -| | `routeTask(task)` | Route to appropriate agent | -| | `getAgentStatus(id)` | Check agent status | -| `SubagentSpawner` | `spawn(type, options)` | Create subagent | -| | `getSubagentTypes()` | List available types | -| `ClaudeCodeIntegration` | `addMessage(message)` | Add message with auto-compact | -| | `spawnSubagent(task)` | Spawn Claude Code subagent | -| | `saveContext(name)` | Persist context | -| `OpenClawIntegration` | `createPipeline(definition)` | Create OpenClaw pipeline | -| | `executeParallelAgents(tasks)` | Execute 4×3 pattern | -| | `createWorkspace(options)` | Isolated workspace | +```typescript +class DelegationSystem { + // Initialization + initialize(): Promise; + registerIntegration(type: IntegrationType, config?): Promise; + + // Main operations + process(request: DelegationRequest): Promise; + quickClassify(content: string): { complexity: string; agent: AgentType }; + + // Progress streaming + onProgress(requestId: string, callback: (event: ProgressEvent) => void): () => void; + onAllProgress(callback: (event: ProgressEvent) => void): () => void; + + // Status + getStatus(): SystemStatus; + getPoolStats(): PoolStats; + isReady(): boolean; + + // Lifecycle + shutdown(): Promise; + reset(): Promise; +} +``` -### Pipeline System API +### Integration Adapters API -| Class | Method | Description | -|-------|--------|-------------| -| `DeterministicStateMachine` | `start(context)` | Start state machine | -| | `transition(event, payload)` | Trigger transition | -| | `getState()` | Get current state | -| | `canTransition(event)` | Check valid transition | -| `ParallelExecutionEngine` | `executeAll(tasks)` | Execute tasks in parallel | -| | `submitTask(task)` | Add to queue | -| | `startWorkers(count)` | Start worker threads | -| `EventBus` | `subscribe(pattern, handler)` | Subscribe to events | -| | `publish(event, data)` | Publish event | -| | `getHistory(filter)` | Get event history | -| `WorkspaceManager` | `createWorkspace(id, options)` | Create workspace | -| | `getWorkspace(id)` | Access workspace | -| | `destroyWorkspace(id)` | Cleanup workspace | -| `YAMLWorkflow` | `parse(yaml)` | Parse workflow definition | -| | `validate()` | Validate workflow | -| | `toStateMachine()` | Convert to state machine | +```typescript +interface IntegrationAdapter { + type: IntegrationType; + name: string; + version: string; + + initialize(config: any): Promise; + shutdown(): Promise; + classifyRequest(request: any): Promise; + delegateRequest(request: any, decision: DelegationDecision): Promise; + streamProgress(requestId: string, callback: (event: ProgressEvent) => void): void; + getStatus(): IntegrationStatus; + getCapabilities(): string[]; +} +``` --- ## Examples -### Example 1: Multi-Project Analysis (OpenClaw Pattern) +### Example 1: Busy-State Handling ```typescript -import { OpenClawIntegration } from './agent-system/integrations/openclaw'; +import { DelegationSystem } from './delegation-system'; -const openclaw = new OpenClawIntegration({ - maxParallelAgents: 12 // 4 projects × 3 roles +const system = new DelegationSystem(); +await system.initialize(); + +// Simulate high load +for (let i = 0; i < 20; i++) { + system.process({ + content: `Task ${i}`, + type: 'code' + }); +} + +// This request will be delegated to available agent or queued +const response = await system.process({ + content: 'Urgent: Fix security vulnerability', + type: 'debug', + priority: 'critical' }); -const projects = ['frontend', 'backend', 'mobile', 'docs']; -const roles = ['security', 'performance', 'quality'] as const; +// Check pool status +const stats = system.getPoolStats(); +console.log(`Pool: ${stats.busyCount} busy, ${stats.idleCount} idle`); +``` -const tasks = projects.flatMap(project => - roles.map(role => ({ - type: 'explorer' as const, - prompt: `Analyze ${project} for ${role} issues`, - context: { project, role } - })) +### Example 2: Progress Streaming + +```typescript +const system = new DelegationSystem(); +await system.initialize(); + +const requestId = 'analysis-1'; + +// Subscribe to progress +const unsubscribe = system.onProgress(requestId, (event) => { + switch (event.type) { + case 'acknowledgment': + console.log('🤖 Got it!'); + break; + case 'delegation': + console.log(`📋 Delegated to ${event.data?.agentType}`); + break; + case 'progress': + console.log(`⏳ ${event.progress}% - ${event.message}`); + break; + case 'completion': + console.log('✅ Done!'); + break; + } +}); + +// Process with streaming +await system.process({ + id: requestId, + content: 'Analyze entire codebase for security issues', + type: 'analysis', + streamProgress: true +}); + +unsubscribe(); +``` + +### Example 3: Multi-Agent Parallel Execution + +```typescript +const system = new DelegationSystem(); +await system.initialize(); + +// OpenClaw pattern: 4 projects × 3 roles +const projects = ['frontend', 'backend', 'mobile', 'docs']; +const roles = ['security', 'performance', 'quality']; + +const results = await Promise.all( + projects.flatMap(project => + roles.map(role => + system.process({ + content: `Analyze ${project} for ${role} issues`, + type: 'analysis', + metadata: { project, role } + }) + ) + ) ); -const results = await openclaw.executeParallelAgents(tasks); - -// Aggregate results by project -for (const [agentId, result] of results) { - console.log(`Agent ${agentId}:`, result.output); -} -``` - -### Example 2: Context-Aware Chat with Claude Code - -```typescript -import { ClaudeCodeIntegration } from './agent-system/integrations/claude-code'; - -class ContextAwareChat { - private claude: ClaudeCodeIntegration; - - constructor() { - this.claude = new ClaudeCodeIntegration({ - maxContextTokens: 200000, - compactionStrategy: 'hybrid', - priorityKeywords: ['important', 'remember', 'decision', 'error'], - autoCompact: true, - compactionThreshold: 0.75 - }); - } - - async chat(userMessage: string): Promise { - // Add user message (auto-compacts if needed) - this.claude.addMessage({ role: 'user', content: userMessage }); - - // Get optimized context for API - const { messages, systemPrompt } = this.claude.getContextForAPI(); - - // ... call Claude API with messages ... - const response = await this.callClaudeAPI(messages, systemPrompt); - - // Add response to context - this.claude.addMessage({ role: 'assistant', content: response }); - - return response; - } - - private async callClaudeAPI(messages: any[], systemPrompt?: string): Promise { - // Your Claude API implementation - return "Response from Claude..."; - } -} -``` - -### Example 3: Human-in-the-Loop Workflow - -```typescript -import { OpenClawIntegration } from './agent-system/integrations/openclaw'; - -const openclaw = new OpenClawIntegration(); - -const pipeline = openclaw.createPipeline({ - name: 'human-approval-workflow', - states: [ - { - name: 'draft', - type: 'sequential', - agents: ['coder'], - transitions: [{ target: 'review', event: 'drafted' }] - }, - { - name: 'review', - type: 'human-approval', - agents: ['reviewer'], - timeout: 86400000, // 24 hours - transitions: [ - { target: 'publish', event: 'approved' }, - { target: 'draft', event: 'rejected' } - ] - }, - { - name: 'publish', - type: 'sequential', - agents: ['executor'], - transitions: [] - } - ] -}); - -await openclaw.startPipeline(pipeline.id); +console.log(`Completed ${results.length} parallel analyses`); ``` --- @@ -599,95 +522,35 @@ await openclaw.startPipeline(pipeline.id); ``` ├── agent-system/ # Context compaction system -│ ├── core/ -│ │ ├── token-counter.ts # Token counting -│ │ ├── summarizer.ts # LLM summarization -│ │ ├── context-manager.ts # Context compaction -│ │ ├── orchestrator.ts # Agent orchestration -│ │ └── subagent-spawner.ts # Subagent creation -│ ├── agents/ -│ │ ├── base-agent.ts # Base agent class -│ │ └── task-agent.ts # Task-specific agent -│ ├── integrations/ -│ │ ├── claude-code.ts # Claude Code integration ✅ -│ │ └── openclaw.ts # OpenClaw integration ✅ -│ ├── storage/ -│ │ └── memory-store.ts # Persistent storage -│ ├── utils/ -│ │ └── helpers.ts # Utility functions -│ └── index.ts # Main exports +│ ├── core/ # Core components +│ ├── agents/ # Agent implementations +│ ├── integrations/ # Claude Code & OpenClaw integrations +│ ├── storage/ # Persistent storage +│ └── utils/ # Utilities │ ├── pipeline-system/ # Deterministic pipeline system -│ ├── core/ -│ │ └── state-machine.ts # State machine -│ ├── engine/ -│ │ └── parallel-executor.ts # Parallel execution -│ ├── events/ -│ │ └── event-bus.ts # Event coordination -│ ├── workspace/ -│ │ └── agent-workspace.ts # Workspace isolation -│ ├── workflows/ -│ │ └── yaml-workflow.ts # YAML parser -│ ├── integrations/ -│ │ └── claude-code.ts # Claude Code integration +│ ├── core/ # State machine +│ ├── engine/ # Parallel execution +│ ├── events/ # Event bus +│ ├── workspace/ # Workspace isolation +│ └── workflows/ # YAML parser +│ +├── delegation-system/ # 🆕 Delegation system +│ ├── core/ # Types, classifier, engine, handoff +│ ├── pool/ # Agent pool management +│ ├── streaming/ # Progress streaming +│ ├── quality/ # Quality gates +│ ├── integrations/ # 3rd party adapters +│ ├── test.ts # Test suite │ └── index.ts # Main exports │ +├── examples/ # Usage examples ├── downloads/ # Zip packages -│ ├── agent-system.zip -│ ├── pipeline-system.zip -│ └── complete-agent-pipeline-system.zip -│ -└── README.md # This file -``` - ---- - -## Compaction Strategies - -### 1. Sliding Window - -Keeps the most recent N messages: - -```typescript -const contextManager = new ContextManager(tokenCounter, summarizer, { - compactionStrategy: 'sliding-window', - slidingWindowSize: 50 // Keep last 50 messages -}); -``` - -### 2. Summarize Old - -Summarizes older messages into a compact summary: - -```typescript -const contextManager = new ContextManager(tokenCounter, summarizer, { - compactionStrategy: 'summarize-old', - preserveRecentCount: 10 // Keep last 10 messages verbatim -}); -``` - -### 3. Priority Retention - -Keeps messages containing priority keywords: - -```typescript -const contextManager = new ContextManager(tokenCounter, summarizer, { - compactionStrategy: 'priority-retention', - priorityKeywords: ['error', 'important', 'decision', 'critical'] -}); -``` - -### 4. Hybrid (Recommended) - -Combines all strategies for optimal results: - -```typescript -const contextManager = new ContextManager(tokenCounter, summarizer, { - compactionStrategy: 'hybrid', - slidingWindowSize: 30, - preserveRecentCount: 10, - priorityKeywords: ['error', 'important', 'decision'] -}); +├── package.json +├── tsconfig.json +├── LICENSE +├── CHANGELOG.md +└── README.md ``` --- diff --git a/agent-system/agents/base-agent.ts b/agent-system/agents/base-agent.ts old mode 100644 new mode 100755 diff --git a/agent-system/agents/task-agent.ts b/agent-system/agents/task-agent.ts old mode 100644 new mode 100755 diff --git a/agent-system/core/context-manager.ts b/agent-system/core/context-manager.ts old mode 100644 new mode 100755 diff --git a/agent-system/core/orchestrator.ts b/agent-system/core/orchestrator.ts old mode 100644 new mode 100755 diff --git a/agent-system/core/subagent-spawner.ts b/agent-system/core/subagent-spawner.ts old mode 100644 new mode 100755 diff --git a/agent-system/core/summarizer.ts b/agent-system/core/summarizer.ts old mode 100644 new mode 100755 diff --git a/agent-system/core/token-counter.ts b/agent-system/core/token-counter.ts old mode 100644 new mode 100755 diff --git a/agent-system/index.ts b/agent-system/index.ts old mode 100644 new mode 100755 diff --git a/agent-system/integrations/claude-code.ts b/agent-system/integrations/claude-code.ts old mode 100644 new mode 100755 diff --git a/agent-system/integrations/openclaw.ts b/agent-system/integrations/openclaw.ts old mode 100644 new mode 100755 diff --git a/agent-system/storage/memory-store.ts b/agent-system/storage/memory-store.ts old mode 100644 new mode 100755 diff --git a/agent-system/utils/helpers.ts b/agent-system/utils/helpers.ts old mode 100644 new mode 100755 diff --git a/delegation-system/core/context-handoff.ts b/delegation-system/core/context-handoff.ts new file mode 100644 index 0000000..351d10b --- /dev/null +++ b/delegation-system/core/context-handoff.ts @@ -0,0 +1,370 @@ +/** + * Context Handoff Protocol + * + * Manages context transfer between agents for seamless delegation. + * Ensures proper serialization, validation, and cleanup of handoffs. + */ + +import { + ContextHandoff, + HandoffResult, + ConversationTurn, + DelegationContext +} from '../core/types'; + +// ============================================================================ +// Handoff Configuration +// ============================================================================ + +interface HandoffConfig { + defaultTimeout: number; + defaultTimeLimit: number; + maxHandoffs: number; + cleanupInterval: number; + handoffTTL: number; +} + +const DEFAULT_CONFIG: HandoffConfig = { + defaultTimeout: 30000, + defaultTimeLimit: 60000, + maxHandoffs: 100, + cleanupInterval: 60000, + handoffTTL: 300000 // 5 minutes +}; + +// ============================================================================ +// Context Handoff Manager Class +// ============================================================================ + +export class ContextHandoffManager { + private config: HandoffConfig; + private activeHandoffs: Map = new Map(); + private completedHandoffs: Map = new Map(); + private cleanupTimer?: NodeJS.Timeout; + private handoffCounter: number = 0; + + constructor(config: Partial = {}) { + this.config = { ...DEFAULT_CONFIG, ...config }; + this.startCleanupTimer(); + } + + // ============================================================================ + // Handoff Creation + // ============================================================================ + + /** + * Create a new context handoff + */ + createHandoff(options: { + sourceAgent: string; + targetAgent: string; + request: any; + files?: Record; + conversationHistory?: ConversationTurn[]; + workspace?: string; + metadata?: Record; + timeLimit?: number; + scope?: 'narrow' | 'medium' | 'broad'; + qualityLevel?: 'fast' | 'balanced' | 'thorough'; + callbackEndpoint?: string; + callbackTimeout?: number; + }): ContextHandoff { + const id = this.generateHandoffId(); + + const handoff: ContextHandoff = { + id, + sourceAgent: options.sourceAgent, + targetAgent: options.targetAgent, + request: options.request, + context: { + files: options.files || {}, + conversationHistory: options.conversationHistory || [], + workspace: options.workspace || '', + metadata: options.metadata || {} + }, + constraints: { + timeLimit: options.timeLimit || this.config.defaultTimeLimit, + scope: options.scope || 'medium', + qualityLevel: options.qualityLevel || 'balanced' + }, + callback: { + endpoint: options.callbackEndpoint || '', + timeout: options.callbackTimeout || this.config.defaultTimeout, + retries: 3 + }, + createdAt: new Date(), + expiresAt: new Date(Date.now() + this.config.handoffTTL) + }; + + this.activeHandoffs.set(id, handoff); + this.handoffCounter++; + + return handoff; + } + + /** + * Create handoff from delegation context + */ + createFromDelegation( + delegationContext: DelegationContext, + sourceAgent: string, + targetAgent: string + ): ContextHandoff { + return this.createHandoff({ + sourceAgent, + targetAgent, + request: delegationContext.originalRequest, + metadata: { + requestId: delegationContext.requestId, + classification: delegationContext.classification, + strategy: delegationContext.delegationDecision.strategy + } + }); + } + + // ============================================================================ + // Handoff Execution + // ============================================================================ + + /** + * Get a handoff by ID + */ + getHandoff(id: string): ContextHandoff | undefined { + return this.activeHandoffs.get(id); + } + + /** + * Complete a handoff with result + */ + completeHandoff( + handoffId: string, + result: { + success: boolean; + output?: any; + error?: string; + processingTime: number; + } + ): HandoffResult { + const handoff = this.activeHandoffs.get(handoffId); + + const handoffResult: HandoffResult = { + handoffId, + success: result.success, + result: result.output, + error: result.error, + processingTime: result.processingTime + }; + + // Store result + this.completedHandoffs.set(handoffId, handoffResult); + + // Remove from active + if (handoff) { + this.activeHandoffs.delete(handoffId); + + // Execute callback if configured + if (handoff.callback.endpoint) { + this.executeCallback(handoff, handoffResult); + } + } + + return handoffResult; + } + + /** + * Cancel a handoff + */ + cancelHandoff(handoffId: string, reason: string): void { + const handoff = this.activeHandoffs.get(handoffId); + + if (handoff) { + this.completeHandoff(handoffId, { + success: false, + error: `Cancelled: ${reason}`, + processingTime: 0 + }); + } + } + + // ============================================================================ + // Callback Execution + // ============================================================================ + + private async executeCallback( + handoff: ContextHandoff, + result: HandoffResult + ): Promise { + const { endpoint, timeout, retries } = handoff.callback; + + for (let attempt = 0; attempt < retries; attempt++) { + try { + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + handoffId: handoff.id, + sourceAgent: handoff.sourceAgent, + targetAgent: handoff.targetAgent, + result + }), + signal: AbortSignal.timeout(timeout) + }); + + if (response.ok) { + return; + } + } catch (error) { + console.error(`Callback attempt ${attempt + 1} failed:`, error); + + // Wait before retry + if (attempt < retries - 1) { + await this.sleep(1000 * (attempt + 1)); + } + } + } + } + + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + // ============================================================================ + // Context Serialization + // ============================================================================ + + /** + * Serialize handoff for transmission + */ + serialize(handoff: ContextHandoff): string { + return JSON.stringify({ + id: handoff.id, + sourceAgent: handoff.sourceAgent, + targetAgent: handoff.targetAgent, + request: handoff.request, + context: handoff.context, + constraints: handoff.constraints, + callback: handoff.callback, + createdAt: handoff.createdAt.toISOString(), + expiresAt: handoff.expiresAt.toISOString() + }); + } + + /** + * Deserialize handoff from transmission + */ + deserialize(data: string): ContextHandoff { + const parsed = JSON.parse(data); + return { + ...parsed, + createdAt: new Date(parsed.createdAt), + expiresAt: new Date(parsed.expiresAt) + }; + } + + /** + * Validate handoff integrity + */ + validate(handoff: ContextHandoff): { valid: boolean; errors: string[] } { + const errors: string[] = []; + + if (!handoff.id) errors.push('Missing handoff ID'); + if (!handoff.sourceAgent) errors.push('Missing source agent'); + if (!handoff.targetAgent) errors.push('Missing target agent'); + if (!handoff.request) errors.push('Missing request'); + + if (handoff.constraints.timeLimit <= 0) { + errors.push('Invalid time limit'); + } + + if (handoff.expiresAt <= new Date()) { + errors.push('Handoff has expired'); + } + + return { + valid: errors.length === 0, + errors + }; + } + + // ============================================================================ + // Cleanup + // ============================================================================ + + private startCleanupTimer(): void { + this.cleanupTimer = setInterval(() => { + this.cleanup(); + }, this.config.cleanupInterval); + } + + private cleanup(): void { + const now = new Date(); + + // Remove expired handoffs + for (const [id, handoff] of this.activeHandoffs) { + if (handoff.expiresAt <= now) { + this.cancelHandoff(id, 'Expired'); + } + } + + // Limit completed handoffs + if (this.completedHandoffs.size > this.config.maxHandoffs) { + const keys = Array.from(this.completedHandoffs.keys()); + const toRemove = keys.slice(0, this.completedHandoffs.size - this.config.maxHandoffs); + toRemove.forEach(key => this.completedHandoffs.delete(key)); + } + } + + /** + * Stop cleanup timer and clear all + */ + shutdown(): void { + if (this.cleanupTimer) { + clearInterval(this.cleanupTimer); + } + this.activeHandoffs.clear(); + this.completedHandoffs.clear(); + } + + // ============================================================================ + // Statistics + // ============================================================================ + + getStats(): { + active: number; + completed: number; + totalCreated: number; + successRate: number; + averageProcessingTime: number; + } { + const completed = Array.from(this.completedHandoffs.values()); + const successful = completed.filter(r => r.success); + + return { + active: this.activeHandoffs.size, + completed: completed.length, + totalCreated: this.handoffCounter, + successRate: completed.length > 0 ? successful.length / completed.length : 0, + averageProcessingTime: completed.length > 0 + ? completed.reduce((sum, r) => sum + r.processingTime, 0) / completed.length + : 0 + }; + } + + private generateHandoffId(): string { + return `handoff-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`; + } +} + +// ============================================================================ +// Factory Function +// ============================================================================ + +export function createContextHandoffManager(config?: Partial): ContextHandoffManager { + return new ContextHandoffManager(config); +} + +// ============================================================================ +// Export +// ============================================================================ + +export default ContextHandoffManager; diff --git a/delegation-system/core/delegation-engine.ts b/delegation-system/core/delegation-engine.ts new file mode 100644 index 0000000..3b8ef3c --- /dev/null +++ b/delegation-system/core/delegation-engine.ts @@ -0,0 +1,642 @@ +/** + * Delegation Engine + * + * Makes intelligent decisions about when and how to delegate requests. + * Routes requests to appropriate agents based on classification and pool state. + */ + +import { + DelegationDecision, + DelegationStrategy, + DelegationContext, + DelegationResult, + RequestClassification, + ClassifiableRequest, + AgentType, + PoolAgent +} from '../core/types'; +import { RequestClassifier } from '../core/request-classifier'; +import { AgentPoolManager } from '../pool/agent-pool-manager'; + +// ============================================================================ +// Delegation Configuration +// ============================================================================ + +interface DelegationConfig { + // Main agent load thresholds + mainAgentBusyThreshold: number; // 0-1, above this = delegate + mainAgentQueueThreshold: number; // max queue before delegation + + // Delegation preferences + preferDelegation: boolean; + maxDelegationTime: number; // ms before fallback + parallelThreshold: number; // complexity score for parallel delegation + + // Fallback settings + fallbackToMain: boolean; + escalationEnabled: boolean; + escalationThreshold: number; // confidence below this triggers escalation +} + +const DEFAULT_CONFIG: DelegationConfig = { + mainAgentBusyThreshold: 0.7, + mainAgentQueueThreshold: 5, + preferDelegation: true, + maxDelegationTime: 60000, + parallelThreshold: 0.6, + fallbackToMain: true, + escalationEnabled: true, + escalationThreshold: 0.7 +}; + +// ============================================================================ +// Main Agent State (simulated) +// ============================================================================ + +interface MainAgentState { + load: number; // 0-1 + queueLength: number; + currentTask?: string; + averageResponseTime: number; +} + +// ============================================================================ +// Delegation Engine Class +// ============================================================================ + +export class DelegationEngine { + private config: DelegationConfig; + private classifier: RequestClassifier; + private poolManager: AgentPoolManager; + private mainAgentState: MainAgentState; + private activeDelegations: Map = new Map(); + private delegationHistory: DelegationContext[] = []; + private maxHistorySize: number = 1000; + + constructor( + classifier: RequestClassifier, + poolManager: AgentPoolManager, + config: Partial = {} + ) { + this.config = { ...DEFAULT_CONFIG, ...config }; + this.classifier = classifier; + this.poolManager = poolManager; + this.mainAgentState = { + load: 0, + queueLength: 0, + averageResponseTime: 5000 + }; + } + + // ============================================================================ + // Delegation Decision + // ============================================================================ + + /** + * Make a delegation decision for a request + */ + async makeDecision( + request: ClassifiableRequest, + classification?: RequestClassification + ): Promise { + // Classify if not provided + if (!classification) { + classification = await this.classifier.classify(request); + } + + // Get current state + const poolStats = this.poolManager.getPoolStats(); + + // Decision tree + const decision = this.evaluateDelegation(request, classification, poolStats); + + return decision; + } + + private evaluateDelegation( + request: ClassifiableRequest, + classification: RequestClassification, + poolStats: ReturnType + ): DelegationDecision { + const mainAgentLoad = this.mainAgentState.load; + const mainAgentQueue = this.mainAgentState.queueLength; + + // Check if can delegate + if (!classification.canDelegate) { + return this.noDelegation(classification, 'Request cannot be delegated'); + } + + // Check if pool has available agents + if (poolStats.idleCount === 0 && poolStats.coolingDownCount === 0) { + if (this.config.fallbackToMain) { + return this.noDelegation(classification, 'No agents available in pool'); + } + return this.queueDelegation(classification, 'Waiting for agent availability'); + } + + // Main agent is lightly loaded - process directly + if (mainAgentLoad < 0.5 && mainAgentQueue < 3) { + if (!this.config.preferDelegation || classification.complexity === 'complex') { + return this.noDelegation(classification, 'Main agent available'); + } + } + + // Determine delegation strategy + const strategy = this.determineStrategy(classification, poolStats); + const targetAgents = this.selectAgents(classification, strategy, poolStats); + const estimatedCompletion = this.estimateCompletion(classification, strategy, poolStats); + + return { + shouldDelegate: true, + strategy, + targetAgents, + estimatedCompletion, + reason: this.getDelegationReason(mainAgentLoad, poolStats), + fallbackPlan: this.config.fallbackToMain ? 'main-agent' : 'queue', + requiresCallback: strategy === 'hierarchical' || classification.complexity === 'complex' + }; + } + + private determineStrategy( + classification: RequestClassification, + poolStats: ReturnType + ): DelegationStrategy { + // Complex requests with high confidence -> hierarchical + if (classification.complexity === 'complex' && classification.confidence > 0.8) { + return 'hierarchical'; + } + + // Multiple files or high complexity score -> parallel + if (classification.contextRequirements.files > 2 || + classification.score > this.config.parallelThreshold) { + if (poolStats.idleCount >= 2) { + return 'parallel'; + } + } + + // Quick requests -> full delegation + if (classification.complexity === 'quick') { + return 'full'; + } + + // Streaming requests with progress -> hybrid + if (classification.complexity === 'streaming') { + return 'hybrid'; + } + + // Default to full delegation + return 'full'; + } + + private selectAgents( + classification: RequestClassification, + strategy: DelegationStrategy, + poolStats: ReturnType + ): AgentType[] { + const primary = classification.recommendedAgent; + const agents: AgentType[] = []; + + switch (strategy) { + case 'full': + agents.push(primary); + break; + + case 'parallel': + // Add primary agent and complementary agents + agents.push(primary); + if (classification.requiredCapabilities.includes('security')) { + agents.push('reviewer'); + } + if (classification.contextRequirements.files > 3) { + agents.push('explorer'); + } + break; + + case 'hierarchical': + // Planner oversees execution + agents.push('planner', primary); + if (classification.requiredCapabilities.includes('security')) { + agents.push('reviewer'); + } + break; + + case 'hybrid': + agents.push(primary); + // Add support agents if available + if (poolStats.idleCount > 2) { + agents.push('fast-responder'); + } + break; + } + + return agents; + } + + private estimateCompletion( + classification: RequestClassification, + strategy: DelegationStrategy, + poolStats: ReturnType + ): number { + let baseTime = classification.estimatedTime; + + // Adjust for strategy + switch (strategy) { + case 'parallel': + // Parallel is faster but has coordination overhead + baseTime = baseTime * 0.6 + 500; + break; + case 'hierarchical': + // Hierarchical has communication overhead + baseTime = baseTime * 1.3; + break; + case 'hybrid': + baseTime = baseTime * 0.8; + break; + } + + // Adjust for pool availability + const availabilityFactor = 1 + (1 - poolStats.availablePercentage) * 0.5; + + return Math.round(baseTime * availabilityFactor); + } + + private getDelegationReason( + mainAgentLoad: number, + poolStats: ReturnType + ): string { + if (mainAgentLoad > 0.8) { + return 'Main agent at high capacity'; + } + if (poolStats.busyPercentage > 0.7) { + return 'Optimal for parallel processing'; + } + return 'Fast delegation path available'; + } + + private noDelegation(classification: RequestClassification, reason: string): DelegationDecision { + return { + shouldDelegate: false, + strategy: 'full', + targetAgents: [classification.recommendedAgent], + estimatedCompletion: classification.estimatedTime, + reason, + requiresCallback: false + }; + } + + private queueDelegation(classification: RequestClassification, reason: string): DelegationDecision { + return { + shouldDelegate: true, + strategy: 'full', + targetAgents: [classification.recommendedAgent], + estimatedCompletion: classification.estimatedTime + 10000, // Add queue time + reason, + fallbackPlan: 'queue', + requiresCallback: true + }; + } + + // ============================================================================ + // Delegation Execution + // ============================================================================ + + /** + * Execute a delegation + */ + async executeDelegation( + request: ClassifiableRequest, + decision: DelegationDecision, + executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise + ): Promise { + const requestId = this.generateRequestId(); + const startTime = Date.now(); + + // Create delegation context + const context: DelegationContext = { + requestId, + originalRequest: request, + classification: await this.classifier.classify(request), + delegationDecision: decision, + assignedAgents: [], + status: 'in-progress', + startTime: new Date() + }; + + this.activeDelegations.set(requestId, context); + + try { + let result: any; + const agentsUsed: string[] = []; + + switch (decision.strategy) { + case 'full': + result = await this.executeFull(request, decision, executor, agentsUsed); + break; + case 'parallel': + result = await this.executeParallel(request, decision, executor, agentsUsed); + break; + case 'hierarchical': + result = await this.executeHierarchical(request, decision, executor, agentsUsed); + break; + case 'hybrid': + result = await this.executeHybrid(request, decision, executor, agentsUsed); + break; + } + + const delegationResult: DelegationResult = { + success: true, + output: result.output || result, + confidence: result.confidence || 0.9, + tokensUsed: result.tokensUsed || 0, + duration: Date.now() - startTime, + agentsUsed, + needsReview: context.classification.complexity === 'complex' + }; + + context.status = 'completed'; + context.endTime = new Date(); + context.result = delegationResult; + + this.addToHistory(context); + return delegationResult; + + } catch (error) { + const delegationResult: DelegationResult = { + success: false, + output: `Delegation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, + confidence: 0, + tokensUsed: 0, + duration: Date.now() - startTime, + agentsUsed: [], + needsReview: true, + escalationReason: error instanceof Error ? error.message : 'Unknown error' + }; + + context.status = 'failed'; + context.endTime = new Date(); + context.result = delegationResult; + + this.addToHistory(context); + return delegationResult; + } finally { + this.activeDelegations.delete(requestId); + } + } + + private async executeFull( + request: ClassifiableRequest, + decision: DelegationDecision, + executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise, + agentsUsed: string[] + ): Promise { + const agentType = decision.targetAgents[0]; + const agent = this.poolManager.acquireAgent(agentType, undefined, request.content.slice(0, 50)); + + if (!agent) { + throw new Error(`No ${agentType} agent available`); + } + + agentsUsed.push(agent.id); + + try { + const result = await executor(agentType, agent.id, request); + return result; + } finally { + this.poolManager.releaseAgent(agent.id); + } + } + + private async executeParallel( + request: ClassifiableRequest, + decision: DelegationDecision, + executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise, + agentsUsed: string[] + ): Promise { + const agents = this.poolManager.acquireAgents(decision.targetAgents, request.content.slice(0, 50)); + + if (agents.length === 0) { + throw new Error('No agents available for parallel execution'); + } + + agents.forEach(a => agentsUsed.push(a.id)); + + // Split request for parallel execution + const subRequests = this.splitRequest(request, agents.length); + + try { + const results = await Promise.all( + agents.map((agent, i) => executor(agent.type, agent.id, subRequests[i] || request)) + ); + + // Merge results + return this.mergeResults(results); + } finally { + agents.forEach(a => this.poolManager.releaseAgent(a.id)); + } + } + + private async executeHierarchical( + request: ClassifiableRequest, + decision: DelegationDecision, + executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise, + agentsUsed: string[] + ): Promise { + // First, planner creates plan + const plannerAgent = this.poolManager.acquireAgent('planner', undefined, request.content.slice(0, 50)); + + if (!plannerAgent) { + return this.executeFull(request, decision, executor, agentsUsed); + } + + agentsUsed.push(plannerAgent.id); + + try { + // Get plan from planner + const plan = await executor('planner', plannerAgent.id, { + ...request, + content: `Create execution plan for: ${request.content}` + }); + + // Execute plan steps with primary agent + const primaryType = decision.targetAgents[decision.targetAgents.indexOf('planner') + 1] || 'coder'; + const primaryAgent = this.poolManager.acquireAgent(primaryType); + + if (primaryAgent) { + agentsUsed.push(primaryAgent.id); + const result = await executor(primaryType, primaryAgent.id, { + ...request, + content: `Execute this plan:\n${plan.output}\n\nOriginal request: ${request.content}` + }); + + this.poolManager.releaseAgent(primaryAgent.id); + return result; + } + + return plan; + } finally { + this.poolManager.releaseAgent(plannerAgent.id); + } + } + + private async executeHybrid( + request: ClassifiableRequest, + decision: DelegationDecision, + executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise, + agentsUsed: string[] + ): Promise { + // Start with fast response, then enhance + const fastAgent = this.poolManager.acquireAgent('fast-responder'); + + if (fastAgent) { + agentsUsed.push(fastAgent.id); + + try { + // Quick initial response + const quickResult = await executor('fast-responder', fastAgent.id, request); + + // Enhanced processing with primary agent + const primaryType = decision.targetAgents[0]; + const primaryAgent = this.poolManager.acquireAgent(primaryType); + + if (primaryAgent) { + agentsUsed.push(primaryAgent.id); + + const enhancedResult = await executor(primaryType, primaryAgent.id, { + ...request, + content: `Enhance this response:\n${quickResult.output}\n\nOriginal: ${request.content}` + }); + + this.poolManager.releaseAgent(primaryAgent.id); + return enhancedResult; + } + + return quickResult; + } finally { + this.poolManager.releaseAgent(fastAgent.id); + } + } + + // Fallback to full delegation + return this.executeFull(request, decision, executor, agentsUsed); + } + + private splitRequest(request: ClassifiableRequest, parts: number): ClassifiableRequest[] { + // Simple splitting by files if available + if (request.files && request.files.length >= parts) { + const filesPerPart = Math.ceil(request.files.length / parts); + return Array.from({ length: parts }, (_, i) => ({ + ...request, + files: request.files?.slice(i * filesPerPart, (i + 1) * filesPerPart) + })); + } + + // Otherwise return the same request for each agent + return Array(parts).fill(request); + } + + private mergeResults(results: any[]): any { + if (results.length === 1) return results[0]; + + return { + output: results.map(r => r.output || r).join('\n\n---\n\n'), + confidence: results.reduce((sum, r) => sum + (r.confidence || 0.9), 0) / results.length, + tokensUsed: results.reduce((sum, r) => sum + (r.tokensUsed || 0), 0) + }; + } + + // ============================================================================ + // Main Agent State Management + // ============================================================================ + + /** + * Update main agent state (called by integration layer) + */ + updateMainAgentState(state: Partial): void { + this.mainAgentState = { ...this.mainAgentState, ...state }; + } + + /** + * Get current main agent state + */ + getMainAgentState(): MainAgentState { + return { ...this.mainAgentState }; + } + + // ============================================================================ + // History and Monitoring + // ============================================================================ + + private addToHistory(context: DelegationContext): void { + this.delegationHistory.push(context); + + if (this.delegationHistory.length > this.maxHistorySize) { + this.delegationHistory.shift(); + } + } + + getActiveDelegations(): DelegationContext[] { + return Array.from(this.activeDelegations.values()); + } + + getDelegationHistory(limit: number = 100): DelegationContext[] { + return this.delegationHistory.slice(-limit); + } + + getStats(): { + totalDelegations: number; + successfulDelegations: number; + failedDelegations: number; + averageDuration: number; + strategyUsage: Record; + } { + const history = this.delegationHistory; + + const stats = { + totalDelegations: history.length, + successfulDelegations: history.filter(h => h.status === 'completed').length, + failedDelegations: history.filter(h => h.status === 'failed').length, + averageDuration: 0, + strategyUsage: { + full: 0, + parallel: 0, + hierarchical: 0, + hybrid: 0 + } as Record + }; + + if (history.length > 0) { + const durations = history + .filter(h => h.result) + .map(h => h.result!.duration); + stats.averageDuration = durations.reduce((a, b) => a + b, 0) / durations.length || 0; + + history.forEach(h => { + stats.strategyUsage[h.delegationDecision.strategy]++; + }); + } + + return stats; + } + + private generateRequestId(): string { + return `del-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`; + } +} + +// ============================================================================ +// Factory Function +// ============================================================================ + +export function createDelegationEngine( + classifier: RequestClassifier, + poolManager: AgentPoolManager, + config?: Partial +): DelegationEngine { + return new DelegationEngine(classifier, poolManager, config); +} + +// ============================================================================ +// Export +// ============================================================================ + +export default DelegationEngine; diff --git a/delegation-system/core/request-classifier.ts b/delegation-system/core/request-classifier.ts new file mode 100644 index 0000000..01f70df --- /dev/null +++ b/delegation-system/core/request-classifier.ts @@ -0,0 +1,379 @@ +/** + * Request Classifier + * + * Fast request analysis for delegation decisions. + * Classifies requests by complexity and determines optimal routing. + */ + +import { + RequestClassification, + RequestComplexity, + ClassifiableRequest, + AgentType +} from './types'; + +// ============================================================================ +// Classification Rules +// ============================================================================ + +interface ClassificationRule { + pattern: RegExp | string; + complexity: RequestComplexity; + agentType: AgentType; + weight: number; +} + +const CLASSIFICATION_RULES: ClassificationRule[] = [ + // Quick patterns - instant response + { pattern: /^(what is|explain|show me|list|get|find)\s/i, complexity: 'quick', agentType: 'fast-responder', weight: 0.9 }, + { pattern: /status|help|version|info$/i, complexity: 'quick', agentType: 'fast-responder', weight: 0.95 }, + { pattern: /^(yes|no|ok|thanks|done)$/i, complexity: 'quick', agentType: 'fast-responder', weight: 0.99 }, + + // Code patterns - moderate complexity + { pattern: /(review|check|analyze|inspect)\s+(this|the|my)?\s*(code|file|function)/i, complexity: 'moderate', agentType: 'reviewer', weight: 0.85 }, + { pattern: /(fix|solve|debug|resolve)\s+/i, complexity: 'moderate', agentType: 'analyzer', weight: 0.8 }, + { pattern: /(add|update|modify|change)\s+(a|the|this)?\s*(function|method|class)/i, complexity: 'moderate', agentType: 'coder', weight: 0.85 }, + + // Research patterns - streaming + { pattern: /(research|investigate|explore|search)\s+/i, complexity: 'streaming', agentType: 'researcher', weight: 0.8 }, + { pattern: /(analyze|audit|examine)\s+(the|entire|whole)?\s*(codebase|project|repo)/i, complexity: 'streaming', agentType: 'explorer', weight: 0.85 }, + + // Complex patterns - need main agent or parallel delegation + { pattern: /(refactor|rewrite|restructure|redesign)\s+/i, complexity: 'complex', agentType: 'planner', weight: 0.9 }, + { pattern: /(implement|build|create|develop)\s+(a|an|the)?\s*(new|feature|system)/i, complexity: 'complex', agentType: 'planner', weight: 0.85 }, + { pattern: /(architect|design|plan)\s+/i, complexity: 'complex', agentType: 'planner', weight: 0.9 }, + { pattern: /migrate|upgrade|port\s+/i, complexity: 'complex', agentType: 'planner', weight: 0.85 }, +]; + +// Keywords that indicate complexity +const COMPLEXITY_INDICATORS = { + high: ['architecture', 'system', 'multiple', 'integrate', 'refactor', 'migrate', 'redesign'], + medium: ['implement', 'feature', 'function', 'module', 'component', 'service', 'api'], + low: ['fix', 'update', 'change', 'add', 'remove', 'rename', 'comment'] +}; + +// Time estimates by complexity (ms) +const TIME_ESTIMATES: Record = { + quick: { min: 100, max: 2000 }, + moderate: { min: 2000, max: 15000 }, + complex: { min: 15000, max: 120000 }, + streaming: { min: 5000, max: 60000 } +}; + +// ============================================================================ +// Request Classifier Class +// ============================================================================ + +export class RequestClassifier { + private cachedClassifications: Map = new Map(); + private readonly cacheMaxSize: number = 1000; + + // Capability mapping by request type + private readonly typeCapabilities: Record = { + code: ['syntax', 'semantics', 'patterns', 'best-practices'], + question: ['knowledge', 'explanation', 'examples'], + task: ['execution', 'planning', 'coordination'], + analysis: ['inspection', 'metrics', 'patterns', 'security'], + review: ['quality', 'standards', 'best-practices', 'security'], + refactor: ['patterns', 'optimization', 'clean-code'], + debug: ['tracing', 'analysis', 'diagnosis'] + }; + + /** + * Classify a request for delegation decisions + */ + async classify(request: ClassifiableRequest): Promise { + // Check cache first + const cacheKey = this.getCacheKey(request); + const cached = this.cachedClassifications.get(cacheKey); + if (cached) { + return { ...cached, confidence: Math.max(0, cached.confidence - 0.1) }; // Slightly lower confidence for cached + } + + // Analyze request + const analysis = await this.analyzeRequest(request); + + // Determine complexity + const complexity = this.determineComplexity(request, analysis); + + // Get recommended agent + const recommendedAgent = this.getRecommendedAgent(request, complexity, analysis); + + // Calculate estimated time + const estimatedTime = this.estimateTime(complexity, analysis); + + // Determine capabilities needed + const requiredCapabilities = this.getRequiredCapabilities(request, analysis); + + // Build classification + const classification: RequestClassification = { + complexity, + score: analysis.complexityScore, + confidence: analysis.confidence, + recommendedAgent, + estimatedTime, + canDelegate: this.canDelegate(complexity, analysis), + delegationPriority: this.getPriority(request, analysis), + requiredCapabilities, + contextRequirements: { + files: analysis.fileCount, + depth: analysis.contextDepth, + history: analysis.needsHistory + } + }; + + // Cache the result + this.cacheClassification(cacheKey, classification); + + return classification; + } + + /** + * Quick classification for fast-path decisions (< 50ms target) + */ + quickClassify(content: string): { complexity: RequestComplexity; agent: AgentType } { + // Fast pattern matching + for (const rule of CLASSIFICATION_RULES) { + if (typeof rule.pattern === 'string') { + if (content.toLowerCase().includes(rule.pattern.toLowerCase())) { + return { complexity: rule.complexity, agent: rule.agentType }; + } + } else { + if (rule.pattern.test(content)) { + return { complexity: rule.complexity, agent: rule.agentType }; + } + } + } + + // Default based on length + const length = content.length; + if (length < 100) return { complexity: 'quick', agent: 'fast-responder' }; + if (length < 500) return { complexity: 'moderate', agent: 'coder' }; + return { complexity: 'complex', agent: 'planner' }; + } + + // ============================================================================ + // Private Methods + // ============================================================================ + + private async analyzeRequest(request: ClassifiableRequest): Promise<{ + complexityScore: number; + confidence: number; + fileCount: number; + contextDepth: 'shallow' | 'medium' | 'deep'; + needsHistory: boolean; + matchedRules: ClassificationRule[]; + keywordDensity: Record; + }> { + const content = request.content.toLowerCase(); + + // Match rules + const matchedRules: ClassificationRule[] = []; + for (const rule of CLASSIFICATION_RULES) { + const pattern = typeof rule.pattern === 'string' + ? new RegExp(rule.pattern, 'i') + : rule.pattern; + if (pattern.test(content)) { + matchedRules.push(rule); + } + } + + // Calculate keyword density + const keywordDensity: Record = {}; + for (const [level, keywords] of Object.entries(COMPLEXITY_INDICATORS)) { + keywordDensity[level] = keywords.reduce((count, kw) => { + return count + (content.includes(kw) ? 1 : 0); + }, 0) / keywords.length; + } + + // Calculate complexity score (0-1) + let complexityScore = 0; + + // Length factor + complexityScore += Math.min(request.content.length / 2000, 0.3); + + // Rule matching factor + if (matchedRules.length > 0) { + const avgWeight = matchedRules.reduce((sum, r) => sum + r.weight, 0) / matchedRules.length; + complexityScore += avgWeight * 0.4; + } + + // Keyword density factor + complexityScore += keywordDensity.high * 0.2; + complexityScore += keywordDensity.medium * 0.1; + + // File count factor + const fileCount = request.files?.length || 0; + complexityScore += Math.min(fileCount / 10, 0.1); + + // Normalize + complexityScore = Math.min(complexityScore, 1); + + // Determine context depth + let contextDepth: 'shallow' | 'medium' | 'deep' = 'shallow'; + if (complexityScore > 0.6 || fileCount > 3) contextDepth = 'deep'; + else if (complexityScore > 0.3 || fileCount > 1) contextDepth = 'medium'; + + // Check if history is needed + const needsHistory = /context|previous|earlier|before|last|history/i.test(content); + + // Calculate confidence + let confidence = 0.5; + if (matchedRules.length > 0) confidence += 0.3; + if (keywordDensity.high > 0 || keywordDensity.medium > 0) confidence += 0.1; + confidence = Math.min(confidence, 0.95); + + return { + complexityScore, + confidence, + fileCount, + contextDepth, + needsHistory, + matchedRules, + keywordDensity + }; + } + + private determineComplexity( + request: ClassifiableRequest, + analysis: { complexityScore: number; matchedRules: ClassificationRule[] } + ): RequestComplexity { + // Check matched rules first + if (analysis.matchedRules.length > 0) { + // Get the highest weight rule's complexity + const topRule = analysis.matchedRules.reduce((best, rule) => + rule.weight > best.weight ? rule : best + ); + return topRule.complexity; + } + + // Fall back to score-based classification + if (analysis.complexityScore < 0.25) return 'quick'; + if (analysis.complexityScore < 0.5) return 'moderate'; + if (analysis.complexityScore < 0.75) return 'streaming'; + return 'complex'; + } + + private getRecommendedAgent( + request: ClassifiableRequest, + complexity: RequestComplexity, + analysis: { matchedRules: ClassificationRule[] } + ): AgentType { + // Check matched rules + if (analysis.matchedRules.length > 0) { + const topRule = analysis.matchedRules.reduce((best, rule) => + rule.weight > best.weight ? rule : best + ); + return topRule.agentType; + } + + // Map request type to agent + const typeToAgent: Record = { + code: 'coder', + question: 'fast-responder', + task: 'executor', + analysis: 'analyzer', + review: 'reviewer', + refactor: 'coder', + debug: 'analyzer' + }; + + return typeToAgent[request.type] || 'fast-responder'; + } + + private estimateTime( + complexity: RequestComplexity, + analysis: { complexityScore: number; fileCount: number } + ): number { + const base = TIME_ESTIMATES[complexity]; + let estimate = base.min + (base.max - base.min) * analysis.complexityScore; + + // Add time for files + estimate += analysis.fileCount * 500; + + return Math.round(estimate); + } + + private canDelegate(complexity: RequestComplexity, analysis: any): boolean { + // Quick and moderate can always be delegated + if (complexity === 'quick' || complexity === 'moderate') return true; + + // Streaming can be delegated with progress + if (complexity === 'streaming') return true; + + // Complex depends on confidence + return analysis.confidence > 0.7; + } + + private getPriority( + request: ClassifiableRequest, + analysis: any + ): 'low' | 'medium' | 'high' | 'critical' { + // Check metadata for explicit priority + if (request.metadata?.priority) { + return request.metadata.priority; + } + + // Determine from analysis + if (analysis.keywordDensity?.high > 0.5) return 'high'; + if (analysis.complexityScore > 0.7) return 'high'; + if (analysis.complexityScore > 0.4) return 'medium'; + return 'low'; + } + + private getRequiredCapabilities( + request: ClassifiableRequest, + analysis: any + ): string[] { + const capabilities = new Set(); + + // Add type-based capabilities + const typeCaps = this.typeCapabilities[request.type] || []; + typeCaps.forEach(c => capabilities.add(c)); + + // Add based on content analysis + if (/security|auth|encrypt/i.test(request.content)) capabilities.add('security'); + if (/test|spec|coverage/i.test(request.content)) capabilities.add('testing'); + if (/performance|optim|speed/i.test(request.content)) capabilities.add('performance'); + if (/doc|comment|readme/i.test(request.content)) capabilities.add('documentation'); + + return Array.from(capabilities); + } + + private getCacheKey(request: ClassifiableRequest): string { + return `${request.type}:${request.content.slice(0, 100)}:${request.files?.length || 0}`; + } + + private cacheClassification(key: string, classification: RequestClassification): void { + // Enforce max size + if (this.cachedClassifications.size >= this.cacheMaxSize) { + // Remove oldest entry + const firstKey = this.cachedClassifications.keys().next().value; + if (firstKey) { + this.cachedClassifications.delete(firstKey); + } + } + this.cachedClassifications.set(key, classification); + } + + /** + * Clear classification cache + */ + clearCache(): void { + this.cachedClassifications.clear(); + } +} + +// ============================================================================ +// Factory Function +// ============================================================================ + +export function createRequestClassifier(): RequestClassifier { + return new RequestClassifier(); +} + +// ============================================================================ +// Export +// ============================================================================ + +export default RequestClassifier; diff --git a/delegation-system/core/types.ts b/delegation-system/core/types.ts new file mode 100644 index 0000000..2acde22 --- /dev/null +++ b/delegation-system/core/types.ts @@ -0,0 +1,334 @@ +/** + * Delegation System Types + * + * Core types for the delegation system that enables fast response + * by delegating requests to subagents when the main agent is busy. + */ + +// ============================================================================ +// Request Classification +// ============================================================================ + +export type RequestComplexity = 'quick' | 'moderate' | 'complex' | 'streaming'; + +export interface RequestClassification { + complexity: RequestComplexity; + score: number; // 0-1 + confidence: number; // 0-1 + recommendedAgent: AgentType; + estimatedTime: number; // milliseconds + canDelegate: boolean; + delegationPriority: 'low' | 'medium' | 'high' | 'critical'; + requiredCapabilities: string[]; + contextRequirements: { + files: number; + depth: 'shallow' | 'medium' | 'deep'; + history: boolean; + }; +} + +export interface ClassifiableRequest { + content: string; + type: 'code' | 'question' | 'task' | 'analysis' | 'review' | 'refactor' | 'debug'; + files?: string[]; + metadata?: Record; + timestamp: number; +} + +// ============================================================================ +// Agent Pool Types +// ============================================================================ + +export type AgentType = + | 'explorer' + | 'researcher' + | 'coder' + | 'reviewer' + | 'planner' + | 'executor' + | 'analyzer' + | 'fast-responder'; + +export type AgentStatus = 'idle' | 'warming-up' | 'busy' | 'cooling-down' | 'error'; + +export interface PoolAgent { + id: string; + type: AgentType; + status: AgentStatus; + capabilities: string[]; + currentTask?: string; + completedTasks: number; + averageResponseTime: number; + successRate: number; + lastUsed?: Date; + createdAt: Date; +} + +export interface AgentPoolConfig { + maxSize: number; + minIdle: number; + warmUpTimeout: number; + coolDownPeriod: number; + scaleUpThreshold: number; // % busy triggers scale up + scaleDownThreshold: number; // % idle triggers scale down + agentConfigs: Record; +} + +export interface AgentSpawnConfig { + type: AgentType; + capabilities: string[]; + maxConcurrent: number; + timeout: number; + retryAttempts: number; + priority: number; +} + +// ============================================================================ +// Delegation Types +// ============================================================================ + +export type DelegationStrategy = + | 'full' // Complete delegation to subagent + | 'parallel' // Multiple subagents work together + | 'hierarchical' // Main agent + subagent collaboration + | 'hybrid'; // Combination based on context + +export interface DelegationDecision { + shouldDelegate: boolean; + strategy: DelegationStrategy; + targetAgents: AgentType[]; + estimatedCompletion: number; + reason: string; + fallbackPlan?: string; + requiresCallback: boolean; +} + +export interface DelegationContext { + requestId: string; + originalRequest: ClassifiableRequest; + classification: RequestClassification; + delegationDecision: DelegationDecision; + assignedAgents: string[]; + status: 'pending' | 'in-progress' | 'completed' | 'failed' | 'escalated'; + startTime: Date; + endTime?: Date; + result?: DelegationResult; +} + +export interface DelegationResult { + success: boolean; + output: string; + confidence: number; + tokensUsed: number; + duration: number; + agentsUsed: string[]; + needsReview: boolean; + escalationReason?: string; +} + +// ============================================================================ +// Progress Streaming Types +// ============================================================================ + +export type ProgressEventType = + | 'acknowledgment' + | 'delegation' + | 'progress' + | 'partial-result' + | 'completion' + | 'error' + | 'escalation'; + +export interface ProgressEvent { + type: ProgressEventType; + requestId: string; + message: string; + progress: number; // 0-100 + timestamp: Date; + data?: { + agentType?: AgentType; + agentId?: string; + currentStep?: string; + totalSteps?: number; + completedSteps?: number; + partialResults?: any[]; + }; +} + +export interface ProgressConfig { + enabled: boolean; + updateInterval: number; // milliseconds + includePartialResults: boolean; + maxQueueSize: number; +} + +// ============================================================================ +// Context Handoff Types +// ============================================================================ + +export interface ContextHandoff { + id: string; + sourceAgent: string; + targetAgent: string; + request: ClassifiableRequest; + context: { + files: Record; + conversationHistory: ConversationTurn[]; + workspace: string; + metadata: Record; + }; + constraints: { + timeLimit: number; + scope: 'narrow' | 'medium' | 'broad'; + qualityLevel: 'fast' | 'balanced' | 'thorough'; + }; + callback: { + endpoint: string; + timeout: number; + retries: number; + }; + createdAt: Date; + expiresAt: Date; +} + +export interface ConversationTurn { + role: 'user' | 'assistant' | 'system'; + content: string; + timestamp: number; +} + +export interface HandoffResult { + handoffId: string; + success: boolean; + result?: any; + error?: string; + processingTime: number; +} + +// ============================================================================ +// Quality Gate Types +// ============================================================================ + +export interface QualityCheck { + id: string; + name: string; + description: string; + check: (result: DelegationResult, context: DelegationContext) => Promise; + severity: 'low' | 'medium' | 'high' | 'critical'; + autoFix: boolean; +} + +export interface QualityCheckResult { + passed: boolean; + score: number; // 0-1 + issues: QualityIssue[]; + recommendations: string[]; +} + +export interface QualityIssue { + severity: 'low' | 'medium' | 'high' | 'critical'; + message: string; + location?: string; + suggestion?: string; +} + +export interface QualityGateConfig { + enabled: boolean; + minConfidence: number; // 0-1 + checks: QualityCheck[]; + escalationThreshold: number; + autoEscalate: boolean; +} + +// ============================================================================ +// Integration Types +// ============================================================================ + +export type IntegrationType = + | 'openclaw' + | 'claude-code' + | 'cursor' + | 'aider' + | 'copilot' + | 'generic'; + +export interface IntegrationAdapter { + type: IntegrationType; + name: string; + version: string; + + // Lifecycle + initialize(config: any): Promise; + shutdown(): Promise; + + // Core operations + classifyRequest(request: any): Promise; + delegateRequest(request: any, decision: DelegationDecision): Promise; + streamProgress(requestId: string, callback: (event: ProgressEvent) => void): void; + + // Status + getStatus(): IntegrationStatus; + getCapabilities(): string[]; +} + +export interface IntegrationStatus { + connected: boolean; + ready: boolean; + agentPoolAvailable: boolean; + currentLoad: number; // 0-1 + queueLength: number; + averageResponseTime: number; +} + +export interface IntegrationConfig { + type: IntegrationType; + delegation: { + enabled: boolean; + autoDelegate: boolean; + strategy: DelegationStrategy; + fallbackToMain: boolean; + }; + pool: AgentPoolConfig; + quality: QualityGateConfig; + progress: ProgressConfig; +} + +// ============================================================================ +// Unified API Types +// ============================================================================ + +export interface DelegationAPIConfig { + integrations: IntegrationConfig[]; + defaultStrategy: DelegationStrategy; + maxConcurrentDelegations: number; + requestTimeout: number; + enableCaching: boolean; + cacheConfig?: { + maxSize: number; + ttl: number; + }; +} + +export interface DelegationRequest { + id: string; + content: string; + type: ClassifiableRequest['type']; + files?: string[]; + metadata?: Record; + priority?: 'low' | 'medium' | 'high' | 'critical'; + preferredAgent?: AgentType; + timeout?: number; + streamProgress?: boolean; +} + +export interface DelegationResponse { + requestId: string; + success: boolean; + result?: string; + classification?: RequestClassification; + delegation?: DelegationDecision; + confidence: number; + processingTime: number; + agentsUsed: string[]; + progress?: ProgressEvent[]; +} diff --git a/delegation-system/index.ts b/delegation-system/index.ts new file mode 100644 index 0000000..08ee04a --- /dev/null +++ b/delegation-system/index.ts @@ -0,0 +1,416 @@ +/** + * Unified Delegation API + * + * Single entry point for all delegation operations. + * Provides easy integration for 3rd party AI coding tools. + */ + +import { + DelegationAPIConfig, + DelegationRequest, + DelegationResponse, + IntegrationType, + AgentType, + RequestClassification, + DelegationDecision, + ProgressEvent, + DelegationResult +} from './core/types'; + +import { RequestClassifier, createRequestClassifier } from './core/request-classifier'; +import { DelegationEngine, createDelegationEngine } from './core/delegation-engine'; +import { AgentPoolManager, createAgentPoolManager } from './pool/agent-pool-manager'; +import { ProgressStreamer, createProgressStreamer } from './streaming/progress-streamer'; +import { QualityGate, createQualityGate } from './quality/quality-gate'; +import { ContextHandoffManager, createContextHandoffManager } from './core/context-handoff'; + +import { + OpenClawAdapter, + ClaudeCodeAdapter, + GenericAdapter, + BaseIntegrationAdapter, + createOpenClawAdapter, + createClaudeCodeAdapter, + createGenericAdapter +} from './integrations/adapters'; + +// ============================================================================ +// Default Configuration +// ============================================================================ + +const DEFAULT_API_CONFIG: DelegationAPIConfig = { + integrations: [], + defaultStrategy: 'hybrid', + maxConcurrentDelegations: 100, + requestTimeout: 120000, + enableCaching: true, + cacheConfig: { + maxSize: 1000, + ttl: 300000 + } +}; + +// ============================================================================ +// Delegation System Class +// ============================================================================ + +export class DelegationSystem { + private config: DelegationAPIConfig; + private classifier: RequestClassifier; + private poolManager: AgentPoolManager; + private delegationEngine: DelegationEngine; + private progressStreamer: ProgressStreamer; + private qualityGate: QualityGate; + private handoffManager: ContextHandoffManager; + private adapters: Map = new Map(); + private initialized: boolean = false; + + constructor(config: Partial = {}) { + this.config = { ...DEFAULT_API_CONFIG, ...config }; + + // Initialize core components + this.classifier = createRequestClassifier(); + this.poolManager = createAgentPoolManager(); + this.progressStreamer = createProgressStreamer(); + this.qualityGate = createQualityGate(); + this.delegationEngine = createDelegationEngine( + this.classifier, + this.poolManager + ); + this.handoffManager = createContextHandoffManager(); + } + + // ============================================================================ + // Initialization + // ============================================================================ + + /** + * Initialize the delegation system + */ + async initialize(): Promise { + if (this.initialized) return; + + // Initialize configured integrations + for (const integrationConfig of this.config.integrations) { + await this.registerIntegration(integrationConfig.type, integrationConfig); + } + + // If no integrations configured, register default ones + if (this.adapters.size === 0) { + await this.registerIntegration('openclaw'); + await this.registerIntegration('claude-code'); + await this.registerIntegration('generic'); + } + + this.initialized = true; + } + + /** + * Register an integration + */ + async registerIntegration( + type: IntegrationType, + config?: any + ): Promise { + let adapter: BaseIntegrationAdapter; + + switch (type) { + case 'openclaw': + adapter = createOpenClawAdapter( + this.classifier, + this.delegationEngine, + this.poolManager, + this.progressStreamer, + this.qualityGate + ); + break; + + case 'claude-code': + adapter = createClaudeCodeAdapter( + this.classifier, + this.delegationEngine, + this.poolManager, + this.progressStreamer, + this.qualityGate + ); + break; + + case 'cursor': + case 'aider': + case 'copilot': + case 'generic': + default: + adapter = createGenericAdapter( + this.classifier, + this.delegationEngine, + this.poolManager, + this.progressStreamer, + this.qualityGate + ); + break; + } + + await adapter.initialize(config || {}); + this.adapters.set(type, adapter); + + return adapter; + } + + // ============================================================================ + // Main API Methods + // ============================================================================ + + /** + * Process a delegation request + */ + async process(request: DelegationRequest): Promise { + if (!this.initialized) { + await this.initialize(); + } + + const startTime = Date.now(); + const requestId = request.id || `req-${Date.now()}`; + + // Determine which integration to use + const integrationType = request.metadata?.integration || 'generic'; + const adapter = this.adapters.get(integrationType as IntegrationType) || + this.adapters.get('generic')!; + + try { + // Classify request + const classification = await adapter.classifyRequest({ + content: request.content, + type: request.type, + files: request.files, + metadata: request.metadata + }); + + // Make delegation decision + const decision = await this.delegationEngine.makeDecision({ + content: request.content, + type: request.type, + files: request.files, + metadata: request.metadata, + timestamp: Date.now() + }, classification); + + // Start progress tracking if requested + if (request.streamProgress) { + this.progressStreamer.startTracking(requestId); + } + + // Execute delegation or process directly + let result: DelegationResult; + + if (decision.shouldDelegate) { + result = await adapter.delegateRequest({ + content: request.content, + files: request.files, + metadata: request.metadata + }, decision); + } else { + // Process with main agent (simulated) + result = { + success: true, + output: `Main agent processed: ${request.content.slice(0, 100)}`, + confidence: 0.95, + tokensUsed: 200, + duration: Date.now() - startTime, + agentsUsed: ['main-agent'], + needsReview: false + }; + } + + // Build response + const response: DelegationResponse = { + requestId, + success: result.success, + result: result.output, + classification, + delegation: decision, + confidence: result.confidence, + processingTime: Date.now() - startTime, + agentsUsed: result.agentsUsed + }; + + // Complete progress tracking + if (request.streamProgress) { + this.progressStreamer.complete(requestId); + } + + return response; + + } catch (error) { + // Error handling + if (request.streamProgress) { + this.progressStreamer.error(requestId, error instanceof Error ? error.message : 'Unknown error'); + } + + return { + requestId, + success: false, + result: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, + confidence: 0, + processingTime: Date.now() - startTime, + agentsUsed: [] + }; + } + } + + /** + * Quick classify a request (fast path) + */ + quickClassify(content: string): { complexity: string; agent: AgentType } { + return this.classifier.quickClassify(content); + } + + /** + * Get an integration adapter + */ + getAdapter(type: IntegrationType): BaseIntegrationAdapter | undefined { + return this.adapters.get(type); + } + + /** + * Get all registered adapters + */ + getAdapters(): Map { + return new Map(this.adapters); + } + + // ============================================================================ + // Progress Streaming + // ============================================================================ + + /** + * Subscribe to progress updates + */ + onProgress( + requestId: string, + callback: (event: ProgressEvent) => void + ): () => void { + return this.progressStreamer.subscribe(requestId, callback); + } + + /** + * Subscribe to all progress events + */ + onAllProgress(callback: (event: ProgressEvent) => void): () => void { + return this.progressStreamer.subscribeAll(callback); + } + + // ============================================================================ + // Status and Statistics + // ============================================================================ + + /** + * Get system status + */ + getStatus(): { + initialized: boolean; + adapters: number; + poolStats: ReturnType; + delegationStats: ReturnType; + qualityStats: ReturnType; + } { + return { + initialized: this.initialized, + adapters: this.adapters.size, + poolStats: this.poolManager.getPoolStats(), + delegationStats: this.delegationEngine.getStats(), + qualityStats: this.qualityGate.getStats() + }; + } + + /** + * Get pool statistics + */ + getPoolStats(): ReturnType { + return this.poolManager.getPoolStats(); + } + + /** + * Check if system is ready + */ + isReady(): boolean { + return this.initialized && this.poolManager.getPoolStats().idleCount > 0; + } + + // ============================================================================ + // Lifecycle + // ============================================================================ + + /** + * Shutdown the system + */ + async shutdown(): Promise { + // Shutdown all adapters + for (const adapter of this.adapters.values()) { + await adapter.shutdown(); + } + + // Shutdown pool + await this.poolManager.shutdown(); + + // Clear progress + this.progressStreamer.clearAll(); + + this.initialized = false; + } + + /** + * Reset the system + */ + async reset(): Promise { + await this.shutdown(); + await this.initialize(); + } +} + +// ============================================================================ +// Factory Function +// ============================================================================ + +let defaultInstance: DelegationSystem | null = null; + +export function createDelegationSystem( + config?: Partial +): DelegationSystem { + return new DelegationSystem(config); +} + +export function getDefaultDelegationSystem(): DelegationSystem { + if (!defaultInstance) { + defaultInstance = new DelegationSystem(); + } + return defaultInstance; +} + +// ============================================================================ +// Convenience Exports +// ============================================================================ + +// Re-export all types +export * from './core/types'; + +// Re-export all components +export { RequestClassifier, createRequestClassifier } from './core/request-classifier'; +export { DelegationEngine, createDelegationEngine } from './core/delegation-engine'; +export { AgentPoolManager, createAgentPoolManager } from './pool/agent-pool-manager'; +export { ProgressStreamer, createProgressStreamer } from './streaming/progress-streamer'; +export { QualityGate, createQualityGate } from './quality/quality-gate'; +export { ContextHandoffManager, createContextHandoffManager } from './core/context-handoff'; +export { + OpenClawAdapter, + ClaudeCodeAdapter, + GenericAdapter, + createOpenClawAdapter, + createClaudeCodeAdapter, + createGenericAdapter +} from './integrations/adapters'; + +// ============================================================================ +// Export +// ============================================================================ + +export default DelegationSystem; diff --git a/delegation-system/integrations/adapters.ts b/delegation-system/integrations/adapters.ts new file mode 100644 index 0000000..123aa70 --- /dev/null +++ b/delegation-system/integrations/adapters.ts @@ -0,0 +1,570 @@ +/** + * Integration Adapters + * + * Provides native integration support for 3rd party AI coding tools. + * Supports OpenClaw, Claude Code CLI, Cursor, Aider, and custom integrations. + */ + +import { + IntegrationAdapter, + IntegrationType, + IntegrationStatus, + IntegrationConfig, + RequestClassification, + DelegationDecision, + DelegationResult, + ProgressEvent, + DelegationRequest, + DelegationResponse, + AgentType +} from '../core/types'; +import { RequestClassifier } from '../core/request-classifier'; +import { DelegationEngine } from '../core/delegation-engine'; +import { AgentPoolManager } from '../pool/agent-pool-manager'; +import { ProgressStreamer } from '../streaming/progress-streamer'; +import { QualityGate } from '../quality/quality-gate'; + +// ============================================================================ +// Base Integration Adapter +// ============================================================================ + +export abstract class BaseIntegrationAdapter implements IntegrationAdapter { + abstract type: IntegrationType; + abstract name: string; + abstract version: string; + + protected classifier: RequestClassifier; + protected delegationEngine: DelegationEngine; + protected poolManager: AgentPoolManager; + protected progressStreamer: ProgressStreamer; + protected qualityGate: QualityGate; + protected initialized: boolean = false; + + constructor( + classifier: RequestClassifier, + delegationEngine: DelegationEngine, + poolManager: AgentPoolManager, + progressStreamer: ProgressStreamer, + qualityGate: QualityGate + ) { + this.classifier = classifier; + this.delegationEngine = delegationEngine; + this.poolManager = poolManager; + this.progressStreamer = progressStreamer; + this.qualityGate = qualityGate; + } + + abstract initialize(config: any): Promise; + abstract shutdown(): Promise; + abstract classifyRequest(request: any): Promise; + abstract delegateRequest(request: any, decision: DelegationDecision): Promise; + abstract streamProgress(requestId: string, callback: (event: ProgressEvent) => void): void; + abstract getStatus(): IntegrationStatus; + abstract getCapabilities(): string[]; + + /** + * Check if adapter is ready + */ + isReady(): boolean { + return this.initialized && this.poolManager.getPoolStats().idleCount > 0; + } +} + +// ============================================================================ +// OpenClaw Integration Adapter +// ============================================================================ + +export class OpenClawAdapter extends BaseIntegrationAdapter { + type: IntegrationType = 'openclaw'; + name = 'OpenClaw Integration'; + version = '1.0.0'; + + private config: IntegrationConfig | null = null; + private eventHandlers: Map void>> = new Map(); + + async initialize(config: IntegrationConfig): Promise { + this.config = config; + + // Configure pool for OpenClaw pattern (4 projects × 3 roles) + this.poolManager = new AgentPoolManager({ + maxSize: 50, + minIdle: 12, + ...config.pool + }); + + this.initialized = true; + } + + async shutdown(): Promise { + await this.poolManager.shutdown(); + this.eventHandlers.clear(); + this.initialized = false; + } + + async classifyRequest(request: any): Promise { + // Convert OpenClaw request format + const classifiableRequest = { + content: request.prompt || request.content || request.message, + type: this.detectRequestType(request), + files: request.files || request.context?.files, + metadata: { + projectId: request.projectId, + conversationId: request.conversationId, + ...request.metadata + }, + timestamp: Date.now() + }; + + return this.classifier.classify(classifiableRequest); + } + + async delegateRequest(request: any, decision: DelegationDecision): Promise { + const requestId = `openclaw-${Date.now()}`; + + // Start progress tracking + this.progressStreamer.startTracking(requestId); + this.progressStreamer.acknowledge(requestId); + + // Execute delegation + const result = await this.delegationEngine.executeDelegation( + { + content: request.prompt || request.content, + type: this.detectRequestType(request), + files: request.files, + metadata: request.metadata, + timestamp: Date.now() + }, + decision, + async (agentType, agentId, req) => { + // Notify delegation + this.progressStreamer.notifyDelegation(requestId, agentType, agentId); + + // Simulate agent execution (would call actual agent in production) + return this.executeOpenClawAgent(agentType, agentId, req, request); + } + ); + + // Validate result + const validation = await this.qualityGate.validate(result, { + requestId, + originalRequest: request, + classification: await this.classifyRequest(request), + delegationDecision: decision, + assignedAgents: result.agentsUsed, + status: result.success ? 'completed' : 'failed', + startTime: new Date(), + result + }); + + // Complete progress + if (validation.passed) { + this.progressStreamer.complete(requestId); + } else { + this.progressStreamer.error(requestId, validation.issues.map(i => i.message).join(', ')); + } + + return result; + } + + private async executeOpenClawAgent( + agentType: AgentType, + agentId: string, + request: any, + originalRequest: any + ): Promise { + const startTime = Date.now(); + + // In production, this would call the actual OpenClaw agent + // For now, return a simulated result + await this.simulateProcessing(100, 2000); + + return { + success: true, + output: `[${agentType}] Processed: ${request.content?.slice(0, 100)}...`, + confidence: 0.85, + tokensUsed: Math.floor(Math.random() * 500) + 100, + duration: Date.now() - startTime, + agentsUsed: [agentId], + needsReview: false + }; + } + + private async simulateProcessing(minMs: number, maxMs: number): Promise { + const delay = Math.floor(Math.random() * (maxMs - minMs)) + minMs; + await new Promise(resolve => setTimeout(resolve, delay)); + } + + streamProgress(requestId: string, callback: (event: ProgressEvent) => void): void { + this.progressStreamer.subscribe(requestId, callback); + } + + getStatus(): IntegrationStatus { + const poolStats = this.poolManager.getPoolStats(); + const mainAgentState = this.delegationEngine.getMainAgentState(); + + return { + connected: this.initialized, + ready: this.isReady(), + agentPoolAvailable: poolStats.idleCount > 0, + currentLoad: poolStats.busyPercentage, + queueLength: 0, + averageResponseTime: mainAgentState.averageResponseTime + }; + } + + getCapabilities(): string[] { + return [ + 'parallel-execution', + 'deterministic-pipeline', + 'workspace-isolation', + 'progress-streaming', + 'quality-validation', + 'auto-escalation', + 'lobster-workflows' + ]; + } + + private detectRequestType(request: any): 'code' | 'question' | 'task' | 'analysis' | 'review' | 'refactor' | 'debug' { + const content = (request.prompt || request.content || '').toLowerCase(); + + if (/review|check|audit/i.test(content)) return 'review'; + if (/debug|fix|error|issue/i.test(content)) return 'debug'; + if (/refactor|rewrite|restructure/i.test(content)) return 'refactor'; + if (/analyze|investigate|examine/i.test(content)) return 'analysis'; + if (/implement|create|build|add/i.test(content)) return 'task'; + if (/\?|what|how|why|when/i.test(content)) return 'question'; + + return 'code'; + } + + // OpenClaw-specific methods + on(event: string, handler: (event: any) => void): void { + if (!this.eventHandlers.has(event)) { + this.eventHandlers.set(event, new Set()); + } + this.eventHandlers.get(event)!.add(handler); + } + + off(event: string, handler: (event: any) => void): void { + this.eventHandlers.get(event)?.delete(handler); + } + + private emit(event: string, data: any): void { + this.eventHandlers.get(event)?.forEach(handler => { + try { + handler(data); + } catch (e) { + console.error('Event handler error:', e); + } + }); + } +} + +// ============================================================================ +// Claude Code CLI Integration Adapter +// ============================================================================ + +export class ClaudeCodeAdapter extends BaseIntegrationAdapter { + type: IntegrationType = 'claude-code'; + name = 'Claude Code CLI Integration'; + version = '1.0.0'; + + private config: IntegrationConfig | null = null; + private tools: any[] = []; + + async initialize(config: IntegrationConfig): Promise { + this.config = config; + + // Register Claude Code specific tools + this.registerDefaultTools(); + + this.initialized = true; + } + + async shutdown(): Promise { + this.tools = []; + this.initialized = false; + } + + private registerDefaultTools(): void { + this.tools = [ + { + name: 'delegate_task', + description: 'Delegate a task to a specialized subagent', + input_schema: { + type: 'object', + properties: { + task_type: { + type: 'string', + enum: ['explorer', 'researcher', 'coder', 'reviewer', 'planner', 'analyzer'] + }, + prompt: { type: 'string' }, + files: { type: 'array', items: { type: 'string' } } + }, + required: ['task_type', 'prompt'] + } + }, + { + name: 'check_pool_status', + description: 'Check the status of the agent pool', + input_schema: { + type: 'object', + properties: {} + } + } + ]; + } + + async classifyRequest(request: any): Promise { + const classifiableRequest = { + content: request.content || request.prompt || request.message, + type: this.detectRequestType(request), + files: request.files || [], + metadata: { + toolUse: request.tool_use, + ...request.metadata + }, + timestamp: Date.now() + }; + + return this.classifier.classify(classifiableRequest); + } + + async delegateRequest(request: any, decision: DelegationDecision): Promise { + const requestId = `claude-code-${Date.now()}`; + + this.progressStreamer.startTracking(requestId); + this.progressStreamer.acknowledge(requestId, 'Claude Code: Processing request...'); + + const result = await this.delegationEngine.executeDelegation( + { + content: request.content || request.prompt, + type: this.detectRequestType(request), + files: request.files || [], + metadata: request.metadata, + timestamp: Date.now() + }, + decision, + async (agentType, agentId, req) => { + this.progressStreamer.notifyDelegation(requestId, agentType, agentId); + return this.executeClaudeCodeAgent(agentType, agentId, req, request); + } + ); + + const validation = await this.qualityGate.validate(result, { + requestId, + originalRequest: request, + classification: await this.classifyRequest(request), + delegationDecision: decision, + assignedAgents: result.agentsUsed, + status: result.success ? 'completed' : 'failed', + startTime: new Date(), + result + }); + + if (validation.shouldEscalate) { + this.progressStreamer.escalate(requestId, validation.issues.map(i => i.message).join(', ')); + } else if (validation.passed) { + this.progressStreamer.complete(requestId); + } + + return result; + } + + private async executeClaudeCodeAgent( + agentType: AgentType, + agentId: string, + request: any, + originalRequest: any + ): Promise { + const startTime = Date.now(); + + await new Promise(resolve => setTimeout(resolve, 500 + Math.random() * 1500)); + + return { + success: true, + output: `[Claude Code - ${agentType}] ${request.content?.slice(0, 100)}`, + confidence: 0.9, + tokensUsed: Math.floor(Math.random() * 300) + 50, + duration: Date.now() - startTime, + agentsUsed: [agentId], + needsReview: agentType === 'planner' + }; + } + + streamProgress(requestId: string, callback: (event: ProgressEvent) => void): void { + this.progressStreamer.subscribe(requestId, callback); + } + + getStatus(): IntegrationStatus { + const poolStats = this.poolManager.getPoolStats(); + + return { + connected: this.initialized, + ready: this.isReady(), + agentPoolAvailable: poolStats.idleCount > 0, + currentLoad: poolStats.busyPercentage, + queueLength: 0, + averageResponseTime: 3000 + }; + } + + getCapabilities(): string[] { + return [ + 'tool-use', + 'context-compaction', + 'subagent-spawning', + 'progress-streaming', + 'quality-validation', + 'auto-compaction', + 'persistent-memory' + ]; + } + + getTools(): any[] { + return this.tools; + } + + private detectRequestType(request: any): 'code' | 'question' | 'task' | 'analysis' | 'review' | 'refactor' | 'debug' { + const content = (request.content || request.prompt || '').toLowerCase(); + + if (/review|check/i.test(content)) return 'review'; + if (/debug|fix|error/i.test(content)) return 'debug'; + if (/refactor/i.test(content)) return 'refactor'; + if (/analyze/i.test(content)) return 'analysis'; + if (/implement|create|build/i.test(content)) return 'task'; + if (/\?|what|how/i.test(content)) return 'question'; + + return 'code'; + } +} + +// ============================================================================ +// Generic Integration Adapter +// ============================================================================ + +export class GenericAdapter extends BaseIntegrationAdapter { + type: IntegrationType = 'generic'; + name = 'Generic Integration'; + version = '1.0.0'; + + private config: IntegrationConfig | null = null; + + async initialize(config: IntegrationConfig): Promise { + this.config = config; + this.initialized = true; + } + + async shutdown(): Promise { + this.initialized = false; + } + + async classifyRequest(request: any): Promise { + return this.classifier.classify({ + content: request.content || request.prompt || String(request), + type: 'code', + files: request.files || [], + metadata: request.metadata || {}, + timestamp: Date.now() + }); + } + + async delegateRequest(request: any, decision: DelegationDecision): Promise { + const requestId = `generic-${Date.now()}`; + + this.progressStreamer.startTracking(requestId); + + const result = await this.delegationEngine.executeDelegation( + { + content: request.content || String(request), + type: 'code', + files: [], + metadata: {}, + timestamp: Date.now() + }, + decision, + async (agentType, agentId, req) => { + return { + success: true, + output: `Processed by ${agentType}`, + confidence: 0.8, + tokensUsed: 100, + duration: 1000, + agentsUsed: [agentId], + needsReview: false + }; + } + ); + + return result; + } + + streamProgress(requestId: string, callback: (event: ProgressEvent) => void): void { + this.progressStreamer.subscribe(requestId, callback); + } + + getStatus(): IntegrationStatus { + const poolStats = this.poolManager.getPoolStats(); + + return { + connected: this.initialized, + ready: this.isReady(), + agentPoolAvailable: poolStats.idleCount > 0, + currentLoad: poolStats.busyPercentage, + queueLength: 0, + averageResponseTime: 5000 + }; + } + + getCapabilities(): string[] { + return ['delegation', 'classification', 'progress-streaming']; + } +} + +// ============================================================================ +// Factory Functions +// ============================================================================ + +export function createOpenClawAdapter( + classifier: RequestClassifier, + delegationEngine: DelegationEngine, + poolManager: AgentPoolManager, + progressStreamer: ProgressStreamer, + qualityGate: QualityGate +): OpenClawAdapter { + return new OpenClawAdapter(classifier, delegationEngine, poolManager, progressStreamer, qualityGate); +} + +export function createClaudeCodeAdapter( + classifier: RequestClassifier, + delegationEngine: DelegationEngine, + poolManager: AgentPoolManager, + progressStreamer: ProgressStreamer, + qualityGate: QualityGate +): ClaudeCodeAdapter { + return new ClaudeCodeAdapter(classifier, delegationEngine, poolManager, progressStreamer, qualityGate); +} + +export function createGenericAdapter( + classifier: RequestClassifier, + delegationEngine: DelegationEngine, + poolManager: AgentPoolManager, + progressStreamer: ProgressStreamer, + qualityGate: QualityGate +): GenericAdapter { + return new GenericAdapter(classifier, delegationEngine, poolManager, progressStreamer, qualityGate); +} + +// ============================================================================ +// Export +// ============================================================================ + +export default { + BaseIntegrationAdapter, + OpenClawAdapter, + ClaudeCodeAdapter, + GenericAdapter, + createOpenClawAdapter, + createClaudeCodeAdapter, + createGenericAdapter +}; diff --git a/delegation-system/pool/agent-pool-manager.ts b/delegation-system/pool/agent-pool-manager.ts new file mode 100644 index 0000000..4349259 --- /dev/null +++ b/delegation-system/pool/agent-pool-manager.ts @@ -0,0 +1,524 @@ +/** + * Agent Pool Manager + * + * Manages a pool of subagents for delegation. + * Handles lifecycle, scaling, and task assignment. + */ + +import { + PoolAgent, + AgentPoolConfig, + AgentStatus, + AgentType, + AgentSpawnConfig +} from '../core/types'; + +// ============================================================================ +// Default Configuration +// ============================================================================ + +const DEFAULT_AGENT_CONFIGS: Record = { + 'fast-responder': { + type: 'fast-responder', + capabilities: ['quick-analysis', 'simple-tasks', 'status-checks'], + maxConcurrent: 10, + timeout: 5000, + retryAttempts: 1, + priority: 1 + }, + 'explorer': { + type: 'explorer', + capabilities: ['code-navigation', 'file-search', 'pattern-matching'], + maxConcurrent: 4, + timeout: 30000, + retryAttempts: 2, + priority: 2 + }, + 'researcher': { + type: 'researcher', + capabilities: ['deep-analysis', 'documentation', 'best-practices'], + maxConcurrent: 3, + timeout: 60000, + retryAttempts: 2, + priority: 3 + }, + 'coder': { + type: 'coder', + capabilities: ['code-generation', 'implementation', 'modification'], + maxConcurrent: 4, + timeout: 45000, + retryAttempts: 2, + priority: 2 + }, + 'reviewer': { + type: 'reviewer', + capabilities: ['code-review', 'quality-check', 'security-audit'], + maxConcurrent: 3, + timeout: 30000, + retryAttempts: 2, + priority: 2 + }, + 'planner': { + type: 'planner', + capabilities: ['architecture', 'planning', 'decomposition'], + maxConcurrent: 2, + timeout: 60000, + retryAttempts: 1, + priority: 4 + }, + 'executor': { + type: 'executor', + capabilities: ['command-execution', 'file-operations', 'safe-modification'], + maxConcurrent: 3, + timeout: 30000, + retryAttempts: 3, + priority: 1 + }, + 'analyzer': { + type: 'analyzer', + capabilities: ['code-analysis', 'debugging', 'performance-profiling'], + maxConcurrent: 3, + timeout: 45000, + retryAttempts: 2, + priority: 2 + } +}; + +const DEFAULT_POOL_CONFIG: AgentPoolConfig = { + maxSize: 50, + minIdle: 8, // At least 8 agents ready + warmUpTimeout: 5000, + coolDownPeriod: 10000, + scaleUpThreshold: 0.7, // Scale up when 70% are busy + scaleDownThreshold: 0.3, // Scale down when only 30% are busy + agentConfigs: DEFAULT_AGENT_CONFIGS +}; + +// ============================================================================ +// Agent Pool Manager Class +// ============================================================================ + +export class AgentPoolManager { + private agents: Map = new Map(); + private config: AgentPoolConfig; + private taskAssignments: Map = new Map(); // taskId -> agentId + private scalingTimer?: NodeJS.Timeout; + private isShuttingDown: boolean = false; + + constructor(config: Partial = {}) { + this.config = { ...DEFAULT_POOL_CONFIG, ...config }; + this.initializePool(); + } + + // ============================================================================ + // Pool Initialization + // ============================================================================ + + private initializePool(): void { + // Create initial agents for each type + const initialCounts: Record = { + 'fast-responder': 4, // More fast responders for quick tasks + 'explorer': 2, + 'researcher': 1, + 'coder': 2, + 'reviewer': 1, + 'planner': 1, + 'executor': 1, + 'analyzer': 1 + }; + + for (const [type, count] of Object.entries(initialCounts)) { + for (let i = 0; i < count; i++) { + this.spawnAgent(type as AgentType); + } + } + + // Start scaling monitor + this.startScalingMonitor(); + } + + private spawnAgent(type: AgentType): PoolAgent | null { + if (this.agents.size >= this.config.maxSize) { + return null; + } + + const config = this.config.agentConfigs[type]; + if (!config) return null; + + const agent: PoolAgent = { + id: this.generateAgentId(type), + type, + status: 'idle', + capabilities: config.capabilities, + completedTasks: 0, + averageResponseTime: 0, + successRate: 1.0, + createdAt: new Date() + }; + + this.agents.set(agent.id, agent); + return agent; + } + + private generateAgentId(type: AgentType): string { + return `${type}-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`; + } + + // ============================================================================ + // Agent Selection + // ============================================================================ + + /** + * Get an available agent for a task + */ + acquireAgent( + type: AgentType, + requiredCapabilities?: string[], + taskId?: string + ): PoolAgent | null { + // Find best matching idle agent + let bestAgent: PoolAgent | null = null; + let bestScore = -1; + + for (const agent of this.agents.values()) { + if (agent.type !== type) continue; + if (agent.status !== 'idle') continue; + + // Check capabilities + if (requiredCapabilities && requiredCapabilities.length > 0) { + const hasAllCapabilities = requiredCapabilities.every( + cap => agent.capabilities.includes(cap) + ); + if (!hasAllCapabilities) continue; + } + + // Score based on performance + const score = this.calculateAgentScore(agent); + if (score > bestScore) { + bestScore = score; + bestAgent = agent; + } + } + + if (bestAgent) { + bestAgent.status = 'busy'; + bestAgent.currentTask = taskId; + bestAgent.lastUsed = new Date(); + + if (taskId) { + this.taskAssignments.set(taskId, bestAgent.id); + } + } + + return bestAgent; + } + + /** + * Acquire multiple agents for parallel execution + */ + acquireAgents( + types: AgentType[], + taskId?: string + ): PoolAgent[] { + const acquired: PoolAgent[] = []; + + for (const type of types) { + const agent = this.acquireAgent(type, undefined, taskId); + if (agent) { + acquired.push(agent); + } + } + + return acquired; + } + + /** + * Release an agent back to the pool + */ + releaseAgent(agentId: string, result?: { + success: boolean; + responseTime: number; + }): void { + const agent = this.agents.get(agentId); + if (!agent) return; + + agent.status = 'cooling-down'; + agent.currentTask = undefined; + + // Update stats + if (result) { + agent.completedTasks++; + agent.successRate = this.updateRunningAverage( + agent.successRate, + result.success ? 1 : 0, + agent.completedTasks + ); + agent.averageResponseTime = this.updateRunningAverage( + agent.averageResponseTime, + result.responseTime, + agent.completedTasks + ); + } + + // Remove task assignment + for (const [taskId, aId] of this.taskAssignments) { + if (aId === agentId) { + this.taskAssignments.delete(taskId); + break; + } + } + + // Schedule return to idle + setTimeout(() => { + if (this.agents.has(agentId) && !this.isShuttingDown) { + const a = this.agents.get(agentId); + if (a && a.status === 'cooling-down') { + a.status = 'idle'; + } + } + }, this.config.coolDownPeriod); + } + + private calculateAgentScore(agent: PoolAgent): number { + // Higher success rate = better + // Lower average response time = better + // More completed tasks = more reliable + const reliabilityScore = agent.successRate * 0.4; + const speedScore = (1 - Math.min(agent.averageResponseTime / 60000, 1)) * 0.3; + const experienceScore = Math.min(agent.completedTasks / 100, 1) * 0.3; + + return reliabilityScore + speedScore + experienceScore; + } + + private updateRunningAverage(current: number, newValue: number, count: number): number { + return current + (newValue - current) / count; + } + + // ============================================================================ + // Pool Scaling + // ============================================================================ + + private startScalingMonitor(): void { + this.scalingTimer = setInterval(() => { + this.checkScaling(); + }, 5000); + } + + private checkScaling(): void { + const stats = this.getPoolStats(); + + // Scale up if needed + if (stats.busyPercentage > this.config.scaleUpThreshold) { + this.scaleUp(); + } + + // Scale down if needed + if (stats.busyPercentage < this.config.scaleDownThreshold && + stats.idleCount > this.config.minIdle * 2) { + this.scaleDown(); + } + } + + private scaleUp(): void { + // Find the type with most demand + const demandByType = this.getDemandByType(); + let highestDemand: { type: AgentType; ratio: number } | null = null; + + for (const [type, demand] of Object.entries(demandByType)) { + if (!highestDemand || demand.ratio > highestDemand.ratio) { + highestDemand = { type: type as AgentType, ratio: demand.ratio }; + } + } + + if (highestDemand && highestDemand.ratio > 0.5) { + this.spawnAgent(highestDemand.type); + } + } + + private scaleDown(): void { + // Find idle agents that haven't been used recently + const now = Date.now(); + const maxIdleTime = 5 * 60 * 1000; // 5 minutes + + for (const [id, agent] of this.agents) { + if (agent.status !== 'idle') continue; + if (!agent.lastUsed) continue; + + if (now - agent.lastUsed.getTime() > maxIdleTime) { + // Don't remove if it would go below minimum + if (this.getPoolStats().idleCount > this.config.minIdle) { + this.agents.delete(id); + break; // Only remove one at a time + } + } + } + } + + private getDemandByType(): Record { + const counts: Record = {}; + + for (const agent of this.agents.values()) { + if (!counts[agent.type]) { + counts[agent.type] = { busy: 0, idle: 0 }; + } + if (agent.status === 'busy') { + counts[agent.type].busy++; + } else if (agent.status === 'idle') { + counts[agent.type].idle++; + } + } + + const result: Record = {}; + for (const [type, count] of Object.entries(counts)) { + const total = count.busy + count.idle; + result[type] = { + ...count, + ratio: total > 0 ? count.busy / total : 0 + }; + } + + return result; + } + + // ============================================================================ + // Pool Statistics + // ============================================================================ + + getPoolStats(): { + total: number; + idleCount: number; + busyCount: number; + coolingDownCount: number; + busyPercentage: number; + availablePercentage: number; + byType: Record; + } { + let idleCount = 0; + let busyCount = 0; + let coolingDownCount = 0; + const byType: Record = {} as any; + + for (const agent of this.agents.values()) { + // Initialize type stats + if (!byType[agent.type]) { + byType[agent.type] = { total: 0, idle: 0, busy: 0 }; + } + byType[agent.type].total++; + + switch (agent.status) { + case 'idle': + idleCount++; + byType[agent.type].idle++; + break; + case 'busy': + busyCount++; + byType[agent.type].busy++; + break; + case 'cooling-down': + coolingDownCount++; + break; + } + } + + const total = this.agents.size; + return { + total, + idleCount, + busyCount, + coolingDownCount, + busyPercentage: total > 0 ? busyCount / total : 0, + availablePercentage: total > 0 ? (idleCount + coolingDownCount) / total : 0, + byType + }; + } + + /** + * Check if pool has available agents + */ + hasAvailableAgent(type: AgentType): boolean { + for (const agent of this.agents.values()) { + if (agent.type === type && agent.status === 'idle') { + return true; + } + } + return false; + } + + /** + * Get available agent count by type + */ + getAvailableCount(type: AgentType): number { + let count = 0; + for (const agent of this.agents.values()) { + if (agent.type === type && agent.status === 'idle') { + count++; + } + } + return count; + } + + // ============================================================================ + // Task Tracking + // ============================================================================ + + getAgentByTask(taskId: string): PoolAgent | undefined { + const agentId = this.taskAssignments.get(taskId); + if (agentId) { + return this.agents.get(agentId); + } + return undefined; + } + + // ============================================================================ + // Lifecycle + // ============================================================================ + + /** + * Shutdown the pool + */ + async shutdown(): Promise { + this.isShuttingDown = true; + + if (this.scalingTimer) { + clearInterval(this.scalingTimer); + } + + // Wait for busy agents to complete (with timeout) + const maxWait = 30000; + const startTime = Date.now(); + + while (Date.now() - startTime < maxWait) { + const stats = this.getPoolStats(); + if (stats.busyCount === 0) break; + await this.sleep(1000); + } + + this.agents.clear(); + this.taskAssignments.clear(); + } + + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + /** + * Get all agents (for debugging/monitoring) + */ + getAllAgents(): PoolAgent[] { + return Array.from(this.agents.values()); + } +} + +// ============================================================================ +// Factory Function +// ============================================================================ + +export function createAgentPoolManager(config?: Partial): AgentPoolManager { + return new AgentPoolManager(config); +} + +// ============================================================================ +// Export +// ============================================================================ + +export default AgentPoolManager; diff --git a/delegation-system/quality/quality-gate.ts b/delegation-system/quality/quality-gate.ts new file mode 100644 index 0000000..5ceefe6 --- /dev/null +++ b/delegation-system/quality/quality-gate.ts @@ -0,0 +1,378 @@ +/** + * Quality Gate + * + * Validates delegation results before returning to users. + * Implements confidence thresholds and automatic escalation. + */ + +import { + QualityCheck, + QualityCheckResult, + QualityIssue, + QualityGateConfig, + DelegationResult, + DelegationContext +} from '../core/types'; + +// ============================================================================ +// Default Configuration +// ============================================================================ + +const DEFAULT_CONFIG: QualityGateConfig = { + enabled: true, + minConfidence: 0.7, + checks: [], + escalationThreshold: 0.5, + autoEscalate: true +}; + +// ============================================================================ +// Built-in Quality Checks +// ============================================================================ + +const BUILT_IN_CHECKS: QualityCheck[] = [ + { + id: 'confidence-check', + name: 'Confidence Threshold', + description: 'Ensures result meets minimum confidence threshold', + severity: 'high', + autoFix: false, + check: async (result: DelegationResult) => { + const passed = result.confidence >= 0.7; + return { + passed, + score: result.confidence, + issues: passed ? [] : [{ + severity: 'high' as const, + message: `Confidence ${result.confidence.toFixed(2)} below threshold 0.70` + }], + recommendations: passed ? [] : ['Consider escalating to main agent'] + }; + } + }, + { + id: 'output-validity', + name: 'Output Validity', + description: 'Checks that output is non-empty and meaningful', + severity: 'critical', + autoFix: false, + check: async (result: DelegationResult) => { + const hasOutput = result.output && result.output.trim().length > 0; + const isNotError = !result.output?.toLowerCase().includes('error'); + + return { + passed: hasOutput && isNotError, + score: hasOutput ? (isNotError ? 1 : 0.5) : 0, + issues: [ + ...(hasOutput ? [] : [{ severity: 'critical' as const, message: 'Empty output' }]), + ...(isNotError ? [] : [{ severity: 'high' as const, message: 'Output contains error message' }]) + ], + recommendations: hasOutput ? [] : ['Regenerate response', 'Escalate to main agent'] + }; + } + }, + { + id: 'response-time', + name: 'Response Time Check', + description: 'Validates response was generated in reasonable time', + severity: 'low', + autoFix: false, + check: async (result: DelegationResult) => { + const maxTime = 120000; // 2 minutes + const passed = result.duration <= maxTime; + + return { + passed, + score: Math.max(0, 1 - result.duration / maxTime), + issues: passed ? [] : [{ + severity: 'low' as const, + message: `Response took ${result.duration}ms (max: ${maxTime}ms)` + }], + recommendations: passed ? [] : ['Consider optimizing agent performance'] + }; + } + }, + { + id: 'success-verification', + name: 'Success Verification', + description: 'Confirms the delegation completed successfully', + severity: 'critical', + autoFix: false, + check: async (result: DelegationResult) => { + return { + passed: result.success, + score: result.success ? 1 : 0, + issues: result.success ? [] : [{ + severity: 'critical' as const, + message: result.escalationReason || 'Delegation failed' + }], + recommendations: result.success ? [] : ['Retry with different agent', 'Escalate to main agent'] + }; + } + } +]; + +// ============================================================================ +// Quality Gate Class +// ============================================================================ + +export class QualityGate { + private config: QualityGateConfig; + private checks: Map = new Map(); + private results: Map = new Map(); + + constructor(config: Partial = {}) { + this.config = { ...DEFAULT_CONFIG, ...config }; + + // Register built-in checks + BUILT_IN_CHECKS.forEach(check => this.registerCheck(check)); + + // Register custom checks + this.config.checks.forEach(check => this.registerCheck(check)); + } + + // ============================================================================ + // Check Registration + // ============================================================================ + + /** + * Register a quality check + */ + registerCheck(check: QualityCheck): void { + this.checks.set(check.id, check); + } + + /** + * Remove a quality check + */ + removeCheck(checkId: string): void { + this.checks.delete(checkId); + } + + /** + * Get all registered checks + */ + getChecks(): QualityCheck[] { + return Array.from(this.checks.values()); + } + + // ============================================================================ + // Result Validation + // ============================================================================ + + /** + * Validate a delegation result + */ + async validate( + result: DelegationResult, + context: DelegationContext + ): Promise<{ + passed: boolean; + overallScore: number; + checkResults: Map; + shouldEscalate: boolean; + issues: QualityIssue[]; + }> { + if (!this.config.enabled) { + return { + passed: result.success, + overallScore: result.confidence, + checkResults: new Map(), + shouldEscalate: false, + issues: [] + }; + } + + const checkResults = new Map(); + const allIssues: QualityIssue[] = []; + let totalScore = 0; + let criticalFailures = 0; + + // Run all checks + for (const [id, check] of this.checks) { + try { + const checkResult = await check.check(result, context); + checkResults.set(id, checkResult); + totalScore += checkResult.score; + + // Collect issues + checkResult.issues.forEach(issue => { + allIssues.push({ + ...issue, + location: issue.location || check.name + }); + }); + + // Count critical failures + if (!checkResult.passed && check.severity === 'critical') { + criticalFailures++; + } + + } catch (error) { + console.error(`Quality check ${id} failed:`, error); + checkResults.set(id, { + passed: false, + score: 0, + issues: [{ + severity: 'medium', + message: `Check failed: ${error instanceof Error ? error.message : 'Unknown error'}` + }], + recommendations: [] + }); + } + } + + // Calculate overall score + const overallScore = totalScore / this.checks.size; + + // Determine if passed + const passed = criticalFailures === 0 && overallScore >= this.config.minConfidence; + + // Determine if should escalate + const shouldEscalate = this.config.autoEscalate && ( + overallScore < this.config.escalationThreshold || + criticalFailures > 0 || + allIssues.some(i => i.severity === 'critical') + ); + + // Store results + if (context.requestId) { + this.results.set(context.requestId, Array.from(checkResults.values())); + } + + return { + passed, + overallScore, + checkResults, + shouldEscalate, + issues: allIssues + }; + } + + /** + * Quick validation for fast path + */ + quickValidate(result: DelegationResult): { + passed: boolean; + shouldEscalate: boolean; + } { + if (!this.config.enabled) { + return { passed: result.success, shouldEscalate: false }; + } + + const passed = result.success && + result.confidence >= this.config.minConfidence && + !!result.output; + + const shouldEscalate = this.config.autoEscalate && ( + !result.success || + result.confidence < this.config.escalationThreshold + ); + + return { passed, shouldEscalate }; + } + + // ============================================================================ + // Escalation Decision + // ============================================================================ + + /** + * Decide if result should be escalated to main agent + */ + shouldEscalate( + result: DelegationResult, + validation: { overallScore: number; issues: QualityIssue[] } + ): { + escalate: boolean; + reason: string; + recommendedAction: string; + } { + if (!this.config.autoEscalate) { + return { + escalate: false, + reason: 'Auto-escalation disabled', + recommendedAction: 'Return result as-is' + }; + } + + // Check confidence + if (result.confidence < this.config.escalationThreshold) { + return { + escalate: true, + reason: `Confidence ${result.confidence.toFixed(2)} below threshold ${this.config.escalationThreshold}`, + recommendedAction: 'Forward to main agent for review' + }; + } + + // Check for critical issues + const criticalIssues = validation.issues.filter(i => i.severity === 'critical'); + if (criticalIssues.length > 0) { + return { + escalate: true, + reason: `Critical issues: ${criticalIssues.map(i => i.message).join(', ')}`, + recommendedAction: 'Escalate with context for resolution' + }; + } + + // Check for high severity issues + const highIssues = validation.issues.filter(i => i.severity === 'high'); + if (highIssues.length > 2) { + return { + escalate: true, + reason: `Multiple high-severity issues: ${highIssues.length}`, + recommendedAction: 'Consider escalation for quality assurance' + }; + } + + return { + escalate: false, + reason: 'Result meets quality standards', + recommendedAction: 'Return result to user' + }; + } + + // ============================================================================ + // Statistics + // ============================================================================ + + getStats(): { + totalChecks: number; + validationHistory: number; + averageScore: number; + escalationRate: number; + } { + const allResults = Array.from(this.results.values()).flat(); + const totalChecks = this.checks.size; + const averageScore = allResults.length > 0 + ? allResults.reduce((sum, r) => sum + r.score, 0) / allResults.length + : 0; + + return { + totalChecks, + validationHistory: this.results.size, + averageScore, + escalationRate: 0 // Would need to track escalations + }; + } + + /** + * Clear validation history + */ + clearHistory(): void { + this.results.clear(); + } +} + +// ============================================================================ +// Factory Function +// ============================================================================ + +export function createQualityGate(config?: Partial): QualityGate { + return new QualityGate(config); +} + +// ============================================================================ +// Export +// ============================================================================ + +export default QualityGate; diff --git a/delegation-system/streaming/progress-streamer.ts b/delegation-system/streaming/progress-streamer.ts new file mode 100644 index 0000000..9bcd267 --- /dev/null +++ b/delegation-system/streaming/progress-streamer.ts @@ -0,0 +1,335 @@ +/** + * Progress Streamer + * + * Provides real-time progress updates for delegation tasks. + * Supports WebSocket and SSE for streaming to clients. + */ + +import { + ProgressEvent, + ProgressEventType, + ProgressConfig, + AgentType +} from '../core/types'; + +// ============================================================================ +// Default Configuration +// ============================================================================ + +const DEFAULT_CONFIG: ProgressConfig = { + enabled: true, + updateInterval: 500, // 500ms between updates + includePartialResults: true, + maxQueueSize: 100 +}; + +// ============================================================================ +// Progress Streamer Class +// ============================================================================ + +export class ProgressStreamer { + private config: ProgressConfig; + private progressQueues: Map = new Map(); + private subscribers: Map void>> = new Map(); + private progress: Map = new Map(); + private steps: Map = new Map(); + + constructor(config: Partial = {}) { + this.config = { ...DEFAULT_CONFIG, ...config }; + } + + // ============================================================================ + // Progress Tracking + // ============================================================================ + + /** + * Start tracking progress for a request + */ + startTracking(requestId: string): void { + this.progressQueues.set(requestId, []); + this.progress.set(requestId, 0); + this.steps.set(requestId, { current: 0, total: 0 }); + } + + /** + * Stop tracking and cleanup + */ + stopTracking(requestId: string): void { + this.progressQueues.delete(requestId); + this.progress.delete(requestId); + this.steps.delete(requestId); + this.subscribers.delete(requestId); + } + + /** + * Emit a progress event + */ + emit( + requestId: string, + type: ProgressEventType, + message: string, + data?: ProgressEvent['data'] + ): void { + if (!this.config.enabled) return; + + const event: ProgressEvent = { + type, + requestId, + message, + progress: this.progress.get(requestId) || 0, + timestamp: new Date(), + data + }; + + // Add to queue + const queue = this.progressQueues.get(requestId) || []; + queue.push(event); + + // Enforce max size + if (queue.length > this.config.maxQueueSize) { + queue.shift(); + } + this.progressQueues.set(requestId, queue); + + // Notify subscribers + this.notifySubscribers(requestId, event); + } + + // ============================================================================ + // Convenience Methods + // ============================================================================ + + /** + * Emit acknowledgment event + */ + acknowledge(requestId: string, message: string = 'Got it! Processing...'): void { + this.emit(requestId, 'acknowledgment', message); + } + + /** + * Emit delegation notification + */ + notifyDelegation( + requestId: string, + agentType: AgentType, + agentId: string + ): void { + this.emit(requestId, 'delegation', `Delegating to ${agentType} agent...`, { + agentType, + agentId + }); + } + + /** + * Update progress + */ + updateProgress( + requestId: string, + progress: number, + currentStep?: string, + data?: Partial + ): void { + this.progress.set(requestId, Math.min(100, Math.max(0, progress))); + + const steps = this.steps.get(requestId); + if (steps && data?.completedSteps !== undefined) { + steps.current = data.completedSteps; + } + + this.emit(requestId, 'progress', currentStep || 'Processing...', { + ...data, + currentStep + }); + } + + /** + * Set total steps for progress calculation + */ + setTotalSteps(requestId: string, total: number): void { + const steps = this.steps.get(requestId); + if (steps) { + steps.total = total; + } + } + + /** + * Emit partial results + */ + partialResult(requestId: string, results: any[]): void { + if (!this.config.includePartialResults) return; + + this.emit(requestId, 'partial-result', 'Found intermediate results...', { + partialResults: results + }); + } + + /** + * Emit completion event + */ + complete(requestId: string, message: string = 'Complete!'): void { + this.progress.set(requestId, 100); + this.emit(requestId, 'completion', message); + + // Cleanup after short delay + setTimeout(() => { + this.stopTracking(requestId); + }, 5000); + } + + /** + * Emit error event + */ + error(requestId: string, message: string): void { + this.emit(requestId, 'error', message); + } + + /** + * Emit escalation event + */ + escalate(requestId: string, reason: string): void { + this.emit(requestId, 'escalation', `Escalating to main agent: ${reason}`); + } + + // ============================================================================ + // Subscription Management + // ============================================================================ + + /** + * Subscribe to progress updates for a request + */ + subscribe( + requestId: string, + callback: (event: ProgressEvent) => void + ): () => void { + if (!this.subscribers.has(requestId)) { + this.subscribers.set(requestId, new Set()); + } + + const subscribers = this.subscribers.get(requestId)!; + subscribers.add(callback); + + // Send any queued events immediately + const queue = this.progressQueues.get(requestId) || []; + queue.forEach(event => callback(event)); + + // Return unsubscribe function + return () => { + subscribers.delete(callback); + if (subscribers.size === 0) { + this.subscribers.delete(requestId); + } + }; + } + + /** + * Subscribe to all progress events + */ + subscribeAll(callback: (event: ProgressEvent) => void): () => void { + // Create a unique key for global subscribers + const globalKey = '__global__'; + + if (!this.subscribers.has(globalKey)) { + this.subscribers.set(globalKey, new Set()); + } + + const subscribers = this.subscribers.get(globalKey)!; + subscribers.add(callback); + + return () => { + subscribers.delete(callback); + }; + } + + private notifySubscribers(requestId: string, event: ProgressEvent): void { + // Notify request-specific subscribers + const requestSubscribers = this.subscribers.get(requestId); + if (requestSubscribers) { + requestSubscribers.forEach(callback => { + try { + callback(event); + } catch (e) { + console.error('Progress subscriber error:', e); + } + }); + } + + // Notify global subscribers + const globalSubscribers = this.subscribers.get('__global__'); + if (globalSubscribers) { + globalSubscribers.forEach(callback => { + try { + callback(event); + } catch (e) { + console.error('Global progress subscriber error:', e); + } + }); + } + } + + // ============================================================================ + // Progress Retrieval + // ============================================================================ + + /** + * Get current progress for a request + */ + getProgress(requestId: string): number { + return this.progress.get(requestId) || 0; + } + + /** + * Get all events for a request + */ + getEvents(requestId: string): ProgressEvent[] { + return this.progressQueues.get(requestId) || []; + } + + /** + * Get step progress + */ + getStepProgress(requestId: string): { current: number; total: number } | undefined { + return this.steps.get(requestId); + } + + // ============================================================================ + // Utility Methods + // ============================================================================ + + /** + * Format progress as string + */ + formatProgress(requestId: string): string { + const progress = this.progress.get(requestId) || 0; + const steps = this.steps.get(requestId); + + if (steps && steps.total > 0) { + return `[${'█'.repeat(Math.floor(progress / 5))}${'░'.repeat(20 - Math.floor(progress / 5))}] ${progress.toFixed(0)}% (${steps.current}/${steps.total})`; + } + + return `[${'█'.repeat(Math.floor(progress / 5))}${'░'.repeat(20 - Math.floor(progress / 5))}] ${progress.toFixed(0)}%`; + } + + /** + * Clear all tracking data + */ + clearAll(): void { + this.progressQueues.clear(); + this.subscribers.clear(); + this.progress.clear(); + this.steps.clear(); + } +} + +// ============================================================================ +// Factory Function +// ============================================================================ + +export function createProgressStreamer(config?: Partial): ProgressStreamer { + return new ProgressStreamer(config); +} + +// ============================================================================ +// Export +// ============================================================================ + +export default ProgressStreamer; diff --git a/delegation-system/test.ts b/delegation-system/test.ts new file mode 100644 index 0000000..df5ad77 --- /dev/null +++ b/delegation-system/test.ts @@ -0,0 +1,227 @@ +/** + * Delegation System Test + * + * Tests all components of the delegation system. + */ + +import { DelegationSystem } from './index'; +import { RequestClassifier } from './core/request-classifier'; +import { AgentPoolManager } from './pool/agent-pool-manager'; +import { DelegationEngine } from './core/delegation-engine'; +import { ProgressStreamer } from './streaming/progress-streamer'; +import { QualityGate } from './quality/quality-gate'; + +// ============================================================================ +// Test Runner +// ============================================================================ + +async function runTests() { + console.log('🧪 Running Delegation System Tests\n'); + console.log('=' .repeat(60)); + + let passed = 0; + let failed = 0; + + // Test 1: Request Classifier + console.log('\n📋 Test 1: Request Classifier'); + try { + const classifier = new RequestClassifier(); + + const quickResult = classifier.quickClassify('What is TypeScript?'); + console.log(` Quick classify: ${quickResult.complexity} -> ${quickResult.agent}`); + + const fullResult = await classifier.classify({ + content: 'Implement a user authentication system with JWT tokens', + type: 'task', + files: ['auth.ts', 'middleware.ts'], + timestamp: Date.now() + }); + console.log(` Full classify: ${fullResult.complexity} (score: ${fullResult.score.toFixed(2)})`); + console.log(` Recommended agent: ${fullResult.recommendedAgent}`); + console.log(` Can delegate: ${fullResult.canDelegate}`); + + console.log(' ✅ PASSED'); + passed++; + } catch (error) { + console.log(` ❌ FAILED: ${error}`); + failed++; + } + + // Test 2: Agent Pool Manager + console.log('\n📋 Test 2: Agent Pool Manager'); + try { + const poolManager = new AgentPoolManager(); + + const initialStats = poolManager.getPoolStats(); + console.log(` Initial pool: ${initialStats.total} agents`); + console.log(` Idle: ${initialStats.idleCount}, Busy: ${initialStats.busyCount}`); + + // Acquire an agent + const agent = poolManager.acquireAgent('coder', undefined, 'test-task'); + if (agent) { + console.log(` Acquired agent: ${agent.id} (${agent.type})`); + + const afterAcquire = poolManager.getPoolStats(); + console.log(` After acquire: ${afterAcquire.idleCount} idle, ${afterAcquire.busyCount} busy`); + + // Release the agent + poolManager.releaseAgent(agent.id, { success: true, responseTime: 1000 }); + console.log(` Released agent`); + } + + console.log(' ✅ PASSED'); + passed++; + } catch (error) { + console.log(` ❌ FAILED: ${error}`); + failed++; + } + + // Test 3: Progress Streamer + console.log('\n📋 Test 3: Progress Streamer'); + try { + const streamer = new ProgressStreamer(); + + const requestId = 'test-123'; + streamer.startTracking(requestId); + + // Subscribe to progress + const unsubscribe = streamer.subscribe(requestId, (event) => { + console.log(` Event: ${event.type} - ${event.message}`); + }); + + // Emit events + streamer.acknowledge(requestId); + streamer.notifyDelegation(requestId, 'coder', 'coder-1'); + streamer.updateProgress(requestId, 50, 'Processing...'); + streamer.complete(requestId); + + const progress = streamer.getProgress(requestId); + console.log(` Final progress: ${progress}%`); + + unsubscribe(); + console.log(' ✅ PASSED'); + passed++; + } catch (error) { + console.log(` ❌ FAILED: ${error}`); + failed++; + } + + // Test 4: Quality Gate + console.log('\n📋 Test 4: Quality Gate'); + try { + const qualityGate = new QualityGate(); + + const goodResult = { + success: true, + output: 'Task completed successfully', + confidence: 0.9, + tokensUsed: 100, + duration: 2000, + agentsUsed: ['agent-1'], + needsReview: false + }; + + const validation = await qualityGate.validate(goodResult, { + requestId: 'test-1', + originalRequest: { content: 'test', type: 'code', timestamp: Date.now() }, + classification: { complexity: 'moderate', score: 0.5, confidence: 0.8, recommendedAgent: 'coder', estimatedTime: 5000, canDelegate: true, delegationPriority: 'medium', requiredCapabilities: [], contextRequirements: { files: 0, depth: 'shallow', history: false } }, + delegationDecision: { shouldDelegate: true, strategy: 'full', targetAgents: ['coder'], estimatedCompletion: 5000, reason: 'test' }, + assignedAgents: ['agent-1'], + status: 'completed', + startTime: new Date() + }); + + console.log(` Validation passed: ${validation.passed}`); + console.log(` Overall score: ${validation.overallScore.toFixed(2)}`); + console.log(` Issues: ${validation.issues.length}`); + + console.log(' ✅ PASSED'); + passed++; + } catch (error) { + console.log(` ❌ FAILED: ${error}`); + failed++; + } + + // Test 5: Full Delegation System + console.log('\n📋 Test 5: Full Delegation System'); + try { + const system = new DelegationSystem(); + await system.initialize(); + + console.log(` System initialized: ${system.isReady()}`); + + const status = system.getStatus(); + console.log(` Pool stats: ${status.poolStats.total} agents, ${status.poolStats.idleCount} idle`); + console.log(` Adapters: ${status.adapters}`); + + // Process a request + const response = await system.process({ + id: 'test-request-1', + content: 'Review this code for security issues', + type: 'review', + streamProgress: true + }); + + console.log(` Response success: ${response.success}`); + console.log(` Processing time: ${response.processingTime}ms`); + console.log(` Confidence: ${response.confidence}`); + console.log(` Agents used: ${response.agentsUsed.join(', ')}`); + + await system.shutdown(); + console.log(' ✅ PASSED'); + passed++; + } catch (error) { + console.log(` ❌ FAILED: ${error}`); + failed++; + } + + // Test 6: Integration Adapters + console.log('\n📋 Test 6: Integration Adapters'); + try { + const system = new DelegationSystem(); + await system.initialize(); + + // Test OpenClaw adapter + const openclaw = system.getAdapter('openclaw'); + if (openclaw) { + console.log(` OpenClaw adapter: ${openclaw.name}`); + console.log(` Capabilities: ${openclaw.getCapabilities().slice(0, 3).join(', ')}...`); + } + + // Test Claude Code adapter + const claudeCode = system.getAdapter('claude-code'); + if (claudeCode) { + console.log(` Claude Code adapter: ${claudeCode.name}`); + console.log(` Capabilities: ${claudeCode.getCapabilities().slice(0, 3).join(', ')}...`); + } + + await system.shutdown(); + console.log(' ✅ PASSED'); + passed++; + } catch (error) { + console.log(` ❌ FAILED: ${error}`); + failed++; + } + + // Summary + console.log('\n' + '=' .repeat(60)); + console.log(`\n📊 Test Results: ${passed} passed, ${failed} failed`); + + if (failed === 0) { + console.log('\n✅ All tests passed!\n'); + return true; + } else { + console.log('\n❌ Some tests failed.\n'); + return false; + } +} + +// Run tests +runTests() + .then((success) => { + process.exit(success ? 0 : 1); + }) + .catch((error) => { + console.error('Test runner error:', error); + process.exit(1); + }); diff --git a/downloads/agent-system.zip b/downloads/agent-system.zip old mode 100644 new mode 100755 diff --git a/downloads/complete-agent-pipeline-system.zip b/downloads/complete-agent-pipeline-system.zip old mode 100644 new mode 100755 diff --git a/downloads/pipeline-system.zip b/downloads/pipeline-system.zip old mode 100644 new mode 100755 diff --git a/examples/01-basic-compaction.ts b/examples/01-basic-compaction.ts old mode 100644 new mode 100755 diff --git a/examples/02-claude-code-integration.ts b/examples/02-claude-code-integration.ts old mode 100644 new mode 100755 diff --git a/examples/03-openclaw-integration.ts b/examples/03-openclaw-integration.ts old mode 100644 new mode 100755 diff --git a/examples/04-state-machine-pipeline.ts b/examples/04-state-machine-pipeline.ts old mode 100644 new mode 100755 diff --git a/package.json b/package.json old mode 100644 new mode 100755 diff --git a/pipeline-system/core/state-machine.ts b/pipeline-system/core/state-machine.ts old mode 100644 new mode 100755 diff --git a/pipeline-system/engine/parallel-executor.ts b/pipeline-system/engine/parallel-executor.ts old mode 100644 new mode 100755 diff --git a/pipeline-system/events/event-bus.ts b/pipeline-system/events/event-bus.ts old mode 100644 new mode 100755 diff --git a/pipeline-system/index.ts b/pipeline-system/index.ts old mode 100644 new mode 100755 diff --git a/pipeline-system/integrations/claude-code.ts b/pipeline-system/integrations/claude-code.ts old mode 100644 new mode 100755 diff --git a/pipeline-system/workflows/yaml-workflow.ts b/pipeline-system/workflows/yaml-workflow.ts old mode 100644 new mode 100755 diff --git a/pipeline-system/workspace/agent-workspace.ts b/pipeline-system/workspace/agent-workspace.ts old mode 100644 new mode 100755 diff --git a/tsconfig.json b/tsconfig.json old mode 100644 new mode 100755