Complete Agent Pipeline System with Claude Code & OpenClaw Integration
- Added Claude Code integration with full context compaction support - Added OpenClaw integration with deterministic pipeline support - Implemented parallel agent execution (4 projects x 3 roles pattern) - Added workspace isolation with permissions and quotas - Implemented Lobster-compatible YAML workflow parser - Added persistent memory store for cross-session context - Created comprehensive README with hero section This project was 100% autonomously built by Z.AI GLM-5
This commit is contained in:
654
agent-system/integrations/claude-code.ts
Normal file
654
agent-system/integrations/claude-code.ts
Normal file
@@ -0,0 +1,654 @@
|
||||
/**
|
||||
* Claude Code Integration
|
||||
*
|
||||
* Provides seamless integration with Claude Code CLI and IDE extensions.
|
||||
* Enables context compaction, subagent spawning, and task orchestration
|
||||
* within Claude Code workflows.
|
||||
*/
|
||||
|
||||
import { ContextManager, ContextManagerConfig } from '../core/context-manager';
|
||||
import { TokenCounter } from '../core/token-counter';
|
||||
import { Summarizer, SummarizerOptions } from '../core/summarizer';
|
||||
import { Orchestrator, OrchestratorConfig, Task } from '../core/orchestrator';
|
||||
import { SubagentSpawner, SubagentType } from '../core/subagent-spawner';
|
||||
import { MemoryStore } from '../storage/memory-store';
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
export interface ClaudeCodeConfig {
|
||||
/** Maximum tokens for context (default: 200000 for Claude models) */
|
||||
maxContextTokens?: number;
|
||||
/** Reserve tokens for response generation */
|
||||
reserveTokens?: number;
|
||||
/** Compaction strategy */
|
||||
compactionStrategy?: 'sliding-window' | 'summarize-old' | 'priority-retention' | 'hybrid';
|
||||
/** Priority keywords for retention */
|
||||
priorityKeywords?: string[];
|
||||
/** Enable automatic compaction */
|
||||
autoCompact?: boolean;
|
||||
/** Compaction threshold percentage (0-1) */
|
||||
compactionThreshold?: number;
|
||||
/** Model identifier for Claude */
|
||||
model?: string;
|
||||
/** Enable subagent spawning */
|
||||
enableSubagents?: boolean;
|
||||
/** Max concurrent subagents */
|
||||
maxSubagents?: number;
|
||||
/** Working directory for Claude Code */
|
||||
workingDirectory?: string;
|
||||
/** Enable persistent memory */
|
||||
persistentMemory?: boolean;
|
||||
/** Memory store path */
|
||||
memoryStorePath?: string;
|
||||
}
|
||||
|
||||
export interface ClaudeMessage {
|
||||
role: 'user' | 'assistant' | 'system';
|
||||
content: string;
|
||||
metadata?: {
|
||||
timestamp?: number;
|
||||
tokens?: number;
|
||||
priority?: number;
|
||||
toolUse?: boolean;
|
||||
fileReferences?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ClaudeToolDefinition {
|
||||
name: string;
|
||||
description: string;
|
||||
input_schema: {
|
||||
type: 'object';
|
||||
properties: Record<string, any>;
|
||||
required?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ClaudeCodeSession {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
lastActivity: Date;
|
||||
messageCount: number;
|
||||
tokenUsage: number;
|
||||
status: 'active' | 'compacted' | 'idle' | 'error';
|
||||
}
|
||||
|
||||
export interface CompactionResult {
|
||||
success: boolean;
|
||||
tokensBefore: number;
|
||||
tokensAfter: number;
|
||||
tokensSaved: number;
|
||||
messagesRemoved: number;
|
||||
summary?: string;
|
||||
keyPoints?: string[];
|
||||
decisions?: string[];
|
||||
}
|
||||
|
||||
export interface SubagentTask {
|
||||
type: SubagentType;
|
||||
prompt: string;
|
||||
context?: Record<string, any>;
|
||||
timeout?: number;
|
||||
priority?: 'low' | 'medium' | 'high';
|
||||
}
|
||||
|
||||
export interface SubagentResult {
|
||||
success: boolean;
|
||||
output: string;
|
||||
tokens: number;
|
||||
duration: number;
|
||||
filesModified?: string[];
|
||||
artifacts?: Record<string, any>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Claude Code Integration Class
|
||||
// ============================================================================
|
||||
|
||||
export class ClaudeCodeIntegration {
|
||||
private contextManager: ContextManager;
|
||||
private tokenCounter: TokenCounter;
|
||||
private summarizer: Summarizer;
|
||||
private orchestrator: Orchestrator | null = null;
|
||||
private subagentSpawner: SubagentSpawner | null = null;
|
||||
private memoryStore: MemoryStore | null = null;
|
||||
private config: Required<ClaudeCodeConfig>;
|
||||
private sessionId: string;
|
||||
private messages: ClaudeMessage[] = [];
|
||||
private toolDefinitions: ClaudeToolDefinition[] = [];
|
||||
private compactionHistory: CompactionResult[] = [];
|
||||
|
||||
constructor(config: ClaudeCodeConfig = {}) {
|
||||
this.config = {
|
||||
maxContextTokens: config.maxContextTokens ?? 200000,
|
||||
reserveTokens: config.reserveTokens ?? 40000,
|
||||
compactionStrategy: config.compactionStrategy ?? 'hybrid',
|
||||
priorityKeywords: config.priorityKeywords ?? [
|
||||
'error', 'important', 'decision', 'critical', 'remember', 'todo', 'fixme'
|
||||
],
|
||||
autoCompact: config.autoCompact ?? true,
|
||||
compactionThreshold: config.compactionThreshold ?? 0.8,
|
||||
model: config.model ?? 'claude-sonnet-4-20250514',
|
||||
enableSubagents: config.enableSubagents ?? true,
|
||||
maxSubagents: config.maxSubagents ?? 6,
|
||||
workingDirectory: config.workingDirectory ?? process.cwd(),
|
||||
persistentMemory: config.persistentMemory ?? true,
|
||||
memoryStorePath: config.memoryStorePath ?? '.claude-code/memory'
|
||||
};
|
||||
|
||||
// Initialize core components
|
||||
this.tokenCounter = new TokenCounter(this.config.maxContextTokens);
|
||||
this.summarizer = new Summarizer();
|
||||
this.contextManager = new ContextManager(
|
||||
this.tokenCounter,
|
||||
this.summarizer,
|
||||
{
|
||||
maxTokens: this.config.maxContextTokens - this.config.reserveTokens,
|
||||
compactionStrategy: this.config.compactionStrategy,
|
||||
priorityKeywords: this.config.priorityKeywords,
|
||||
reserveTokens: this.config.reserveTokens
|
||||
}
|
||||
);
|
||||
|
||||
// Initialize orchestrator if subagents enabled
|
||||
if (this.config.enableSubagents) {
|
||||
this.orchestrator = new Orchestrator({
|
||||
maxAgents: this.config.maxSubagents,
|
||||
taskTimeout: 300000,
|
||||
retryAttempts: 3
|
||||
});
|
||||
this.subagentSpawner = new SubagentSpawner();
|
||||
}
|
||||
|
||||
// Initialize memory store if persistent
|
||||
if (this.config.persistentMemory) {
|
||||
this.memoryStore = new MemoryStore(this.config.memoryStorePath);
|
||||
}
|
||||
|
||||
this.sessionId = this.generateSessionId();
|
||||
this.registerDefaultTools();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Session Management
|
||||
// ============================================================================
|
||||
|
||||
private generateSessionId(): string {
|
||||
return `claude-code-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
getSessionId(): string {
|
||||
return this.sessionId;
|
||||
}
|
||||
|
||||
getSessionInfo(): ClaudeCodeSession {
|
||||
const usage = this.tokenCounter.getUsagePercentage();
|
||||
return {
|
||||
id: this.sessionId,
|
||||
createdAt: new Date(parseInt(this.sessionId.split('-')[2])),
|
||||
lastActivity: new Date(),
|
||||
messageCount: this.messages.length,
|
||||
tokenUsage: this.tokenCounter.getCurrentUsage(),
|
||||
status: usage > this.config.compactionThreshold ? 'compacted' :
|
||||
this.messages.length === 0 ? 'idle' : 'active'
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Message Handling
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Add a message to the context
|
||||
*/
|
||||
addMessage(message: ClaudeMessage): void {
|
||||
// Estimate tokens for this message
|
||||
const tokens = this.tokenCounter.countTokens(message.content);
|
||||
message.metadata = {
|
||||
...message.metadata,
|
||||
timestamp: message.metadata?.timestamp ?? Date.now(),
|
||||
tokens
|
||||
};
|
||||
|
||||
this.messages.push(message);
|
||||
this.tokenCounter.addUsage(tokens);
|
||||
|
||||
// Add to context manager
|
||||
this.contextManager.addMessage({
|
||||
role: message.role,
|
||||
content: message.content,
|
||||
priority: message.metadata?.priority,
|
||||
timestamp: message.metadata?.timestamp
|
||||
});
|
||||
|
||||
// Check for auto-compaction
|
||||
if (this.config.autoCompact && this.needsCompaction()) {
|
||||
this.compact();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all messages in context
|
||||
*/
|
||||
getMessages(): ClaudeMessage[] {
|
||||
return [...this.messages];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get context for Claude API call
|
||||
*/
|
||||
getContextForAPI(): { messages: ClaudeMessage[]; systemPrompt?: string } {
|
||||
const activeContext = this.contextManager.getActiveContext();
|
||||
|
||||
const messages: ClaudeMessage[] = activeContext.messages.map(m => ({
|
||||
role: m.role as 'user' | 'assistant',
|
||||
content: m.content,
|
||||
metadata: {
|
||||
timestamp: m.timestamp,
|
||||
priority: m.priority
|
||||
}
|
||||
}));
|
||||
|
||||
return {
|
||||
messages,
|
||||
systemPrompt: activeContext.summary ?
|
||||
`[Previous Context Summary]\n${activeContext.summary}\n\n[End of Summary]` :
|
||||
undefined
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Context Compaction
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Check if compaction is needed
|
||||
*/
|
||||
needsCompaction(): boolean {
|
||||
return this.tokenCounter.getUsagePercentage() >= this.config.compactionThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform context compaction
|
||||
*/
|
||||
async compact(): Promise<CompactionResult> {
|
||||
const tokensBefore = this.tokenCounter.getCurrentUsage();
|
||||
|
||||
try {
|
||||
const result = await this.contextManager.compact();
|
||||
|
||||
// Update local messages to match compacted context
|
||||
const activeContext = this.contextManager.getActiveContext();
|
||||
this.messages = activeContext.messages.map(m => ({
|
||||
role: m.role as 'user' | 'assistant',
|
||||
content: m.content,
|
||||
metadata: {
|
||||
timestamp: m.timestamp,
|
||||
priority: m.priority
|
||||
}
|
||||
}));
|
||||
|
||||
// Reset token counter and recalculate
|
||||
this.tokenCounter.reset();
|
||||
const newTokens = this.messages.reduce(
|
||||
(sum, m) => sum + this.tokenCounter.countTokens(m.content),
|
||||
0
|
||||
);
|
||||
this.tokenCounter.addUsage(newTokens);
|
||||
|
||||
const compactionResult: CompactionResult = {
|
||||
success: true,
|
||||
tokensBefore,
|
||||
tokensAfter: this.tokenCounter.getCurrentUsage(),
|
||||
tokensSaved: tokensBefore - this.tokenCounter.getCurrentUsage(),
|
||||
messagesRemoved: result.messagesRemoved,
|
||||
summary: result.summary,
|
||||
keyPoints: result.keyPoints,
|
||||
decisions: result.decisions
|
||||
};
|
||||
|
||||
this.compactionHistory.push(compactionResult);
|
||||
return compactionResult;
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
tokensBefore,
|
||||
tokensAfter: tokensBefore,
|
||||
tokensSaved: 0,
|
||||
messagesRemoved: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get compaction history
|
||||
*/
|
||||
getCompactionHistory(): CompactionResult[] {
|
||||
return [...this.compactionHistory];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current token usage stats
|
||||
*/
|
||||
getTokenStats(): {
|
||||
used: number;
|
||||
total: number;
|
||||
remaining: number;
|
||||
percentage: number;
|
||||
} {
|
||||
return {
|
||||
used: this.tokenCounter.getCurrentUsage(),
|
||||
total: this.config.maxContextTokens,
|
||||
remaining: this.tokenCounter.getRemainingBudget(),
|
||||
percentage: this.tokenCounter.getUsagePercentage() * 100
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tool Registration
|
||||
// ============================================================================
|
||||
|
||||
private registerDefaultTools(): void {
|
||||
// Register context management tools
|
||||
this.registerTool({
|
||||
name: 'compact_context',
|
||||
description: 'Compact the conversation context to save tokens while preserving important information',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
force: {
|
||||
type: 'boolean',
|
||||
description: 'Force compaction even if threshold not reached'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Register subagent tools
|
||||
if (this.config.enableSubagents) {
|
||||
this.registerTool({
|
||||
name: 'spawn_explorer',
|
||||
description: 'Spawn an explorer agent to quickly search and navigate the codebase',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: {
|
||||
type: 'string',
|
||||
description: 'Search query or file pattern to explore'
|
||||
}
|
||||
},
|
||||
required: ['query']
|
||||
}
|
||||
});
|
||||
|
||||
this.registerTool({
|
||||
name: 'spawn_researcher',
|
||||
description: 'Spawn a researcher agent to deeply analyze and research a topic',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
topic: {
|
||||
type: 'string',
|
||||
description: 'Topic to research'
|
||||
},
|
||||
depth: {
|
||||
type: 'string',
|
||||
enum: ['shallow', 'medium', 'deep'],
|
||||
description: 'Research depth level'
|
||||
}
|
||||
},
|
||||
required: ['topic']
|
||||
}
|
||||
});
|
||||
|
||||
this.registerTool({
|
||||
name: 'spawn_coder',
|
||||
description: 'Spawn a coder agent to implement or modify code',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
task: {
|
||||
type: 'string',
|
||||
description: 'Coding task description'
|
||||
},
|
||||
files: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Files to work on'
|
||||
}
|
||||
},
|
||||
required: ['task']
|
||||
}
|
||||
});
|
||||
|
||||
this.registerTool({
|
||||
name: 'spawn_reviewer',
|
||||
description: 'Spawn a reviewer agent to review code quality and suggest improvements',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
files: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Files to review'
|
||||
},
|
||||
focus: {
|
||||
type: 'string',
|
||||
description: 'Review focus area (security, performance, style, all)'
|
||||
}
|
||||
},
|
||||
required: ['files']
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom tool
|
||||
*/
|
||||
registerTool(tool: ClaudeToolDefinition): void {
|
||||
this.toolDefinitions.push(tool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered tools
|
||||
*/
|
||||
getTools(): ClaudeToolDefinition[] {
|
||||
return [...this.toolDefinitions];
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Subagent Spawning
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Spawn a subagent for a specific task
|
||||
*/
|
||||
async spawnSubagent(task: SubagentTask): Promise<SubagentResult> {
|
||||
if (!this.subagentSpawner || !this.orchestrator) {
|
||||
throw new Error('Subagents are not enabled in this configuration');
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
// Create subagent
|
||||
const agent = this.subagentSpawner.spawn(task.type, {
|
||||
taskId: `${this.sessionId}-${task.type}-${Date.now()}`,
|
||||
memory: this.memoryStore || undefined
|
||||
});
|
||||
|
||||
// Execute task
|
||||
const result = await agent.execute({
|
||||
prompt: task.prompt,
|
||||
...task.context
|
||||
});
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
const outputTokens = this.tokenCounter.countTokens(result.output || '');
|
||||
|
||||
return {
|
||||
success: result.success !== false,
|
||||
output: result.output || '',
|
||||
tokens: outputTokens,
|
||||
duration,
|
||||
filesModified: result.filesModified,
|
||||
artifacts: result.artifacts
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
output: `Subagent error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
tokens: 0,
|
||||
duration: Date.now() - startTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute multiple subagent tasks in parallel
|
||||
*/
|
||||
async executeParallelSubagents(tasks: SubagentTask[]): Promise<SubagentResult[]> {
|
||||
return Promise.all(tasks.map(task => this.spawnSubagent(task)));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Memory Management
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Store a value in persistent memory
|
||||
*/
|
||||
async remember(key: string, value: any): Promise<void> {
|
||||
if (this.memoryStore) {
|
||||
await this.memoryStore.set(`session:${this.sessionId}:${key}`, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a value from persistent memory
|
||||
*/
|
||||
async recall<T>(key: string): Promise<T | null> {
|
||||
if (this.memoryStore) {
|
||||
return this.memoryStore.get<T>(`session:${this.sessionId}:${key}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store important context for cross-session persistence
|
||||
*/
|
||||
async saveContext(name: string): Promise<void> {
|
||||
if (this.memoryStore) {
|
||||
await this.memoryStore.set(`context:${name}`, {
|
||||
sessionId: this.sessionId,
|
||||
messages: this.messages,
|
||||
createdAt: Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a previously saved context
|
||||
*/
|
||||
async loadContext(name: string): Promise<boolean> {
|
||||
if (this.memoryStore) {
|
||||
const saved = await this.memoryStore.get<{
|
||||
sessionId: string;
|
||||
messages: ClaudeMessage[];
|
||||
}>(`context:${name}`);
|
||||
|
||||
if (saved) {
|
||||
this.messages = saved.messages;
|
||||
this.tokenCounter.reset();
|
||||
for (const msg of this.messages) {
|
||||
this.tokenCounter.addUsage(this.tokenCounter.countTokens(msg.content));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Utility Methods
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Reset the session
|
||||
*/
|
||||
reset(): void {
|
||||
this.messages = [];
|
||||
this.tokenCounter.reset();
|
||||
this.contextManager = new ContextManager(
|
||||
this.tokenCounter,
|
||||
this.summarizer,
|
||||
{
|
||||
maxTokens: this.config.maxContextTokens - this.config.reserveTokens,
|
||||
compactionStrategy: this.config.compactionStrategy,
|
||||
priorityKeywords: this.config.priorityKeywords,
|
||||
reserveTokens: this.config.reserveTokens
|
||||
}
|
||||
);
|
||||
this.sessionId = this.generateSessionId();
|
||||
this.compactionHistory = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Export session data
|
||||
*/
|
||||
exportSession(): {
|
||||
sessionId: string;
|
||||
config: Required<ClaudeCodeConfig>;
|
||||
messages: ClaudeMessage[];
|
||||
compactionHistory: CompactionResult[];
|
||||
toolDefinitions: ClaudeToolDefinition[];
|
||||
} {
|
||||
return {
|
||||
sessionId: this.sessionId,
|
||||
config: this.config,
|
||||
messages: this.messages,
|
||||
compactionHistory: this.compactionHistory,
|
||||
toolDefinitions: this.toolDefinitions
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Import session data
|
||||
*/
|
||||
importSession(data: ReturnType<typeof this.exportSession>): void {
|
||||
this.sessionId = data.sessionId;
|
||||
this.messages = data.messages;
|
||||
this.compactionHistory = data.compactionHistory;
|
||||
this.toolDefinitions = data.toolDefinitions;
|
||||
|
||||
// Rebuild token counter state
|
||||
this.tokenCounter.reset();
|
||||
for (const msg of this.messages) {
|
||||
this.tokenCounter.addUsage(this.tokenCounter.countTokens(msg.content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Factory Function
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Create a Claude Code integration instance with sensible defaults
|
||||
*/
|
||||
export function createClaudeCodeIntegration(
|
||||
config?: ClaudeCodeConfig
|
||||
): ClaudeCodeIntegration {
|
||||
return new ClaudeCodeIntegration(config);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Export
|
||||
// ============================================================================
|
||||
|
||||
export default ClaudeCodeIntegration;
|
||||
Reference in New Issue
Block a user