Files
JC Beasley 0bd719ab91 Add cross-project pattern registry for retrieval-augmented generalization
- Create patterns/ directory with README, manifest, and 10 initial patterns
  covering Ollama JSON fallback, API escaping, deprecation, PTY auth,
  queue-poll, LLM-as-parser, credential rotation, reverse proxy binding,
  human approval gates, and transient retry.
- Wire pattern loading into architecture/pipeline.js based on task tags.
- Update architecture/orchestrator.js to load patterns and surface them in
  the system prompt.
- Update MEMORY.md, ARCHITECTURE.md, and CONTEXT.md to document the registry
  and record the decision.
2026-08-06 12:46:09 -07:00

220 lines
5.5 KiB
JavaScript

#!/usr/bin/env node
/**
* Orchestrator - Main Integration Point
*
* Ties together: Pipeline → Router → LLM → Validator → Format Locker
*/
const ContextPipeline = require('./pipeline');
const WorkflowRouter = require('./workflow-router');
const ResponseValidator = require('./validator');
const FormatLocker = require('./format-locker');
class AgentOrchestrator {
constructor(options = {}) {
this.workspace = options.workspace || '/home/jcbeasley/.openclaw/workspace';
this.verbose = options.verbose || false;
this.autoFix = options.autoFix !== false;
this.pipeline = new ContextPipeline();
this.router = new WorkflowRouter();
}
/**
* Main entry point - process user request through full pipeline
*/
async process(userInput, context = {}) {
const startTime = Date.now();
this.log('=== Starting Orchestration ===');
this.log(`Input: ${userInput.substring(0, 100)}...`);
try {
// Step 1: Build context
this.log('Step 1: Building context...');
const contextPacket = await this.buildContext(userInput, context);
// Step 2: Classify intent and route
this.log('Step 2: Classifying intent...');
const route = this.router.route(userInput, context);
// Step 3: Prepare system prompt
this.log(`Step 3: Workflow = ${route.workflow} (${(route.confidence * 100).toFixed(1)}% confidence)`);
const systemPrompt = this.buildSystemPrompt(route, contextPacket);
// Step 4: Get response (this would call LLM)
// For now, return the prompt for testing
const response = {
systemPrompt: systemPrompt,
workflow: route.workflow,
confidence: route.confidence,
context: contextPacket
};
// Step 5: Validate response
this.log('Step 4: Validating...');
const validation = this.validateResponse(response.systemPrompt, route.workflow);
// Step 6: Enforce format
this.log('Step 5: Enforcing format...');
const formatted = this.enforceFormat(response.systemPrompt, route.workflow);
const duration = Date.now() - startTime;
this.log(`=== Complete in ${duration}ms ===`);
return {
...response,
validation: validation,
format: formatted,
metadata: {
duration: duration,
timestamp: new Date().toISOString()
}
};
} catch (error) {
console.error('Orchestration error:', error);
return {
error: error.message,
input: userInput,
metadata: {
duration: Date.now() - startTime,
timestamp: new Date().toISOString()
}
};
}
}
/**
* Build context packet from all sources
*/
async buildContext(userInput, context) {
// Load rules
await this.pipeline.loadRules();
// Load preferences
await this.pipeline.loadPreferences();
// Load relevant memory
await this.pipeline.loadRelevantMemory(userInput, 5);
// Load cross-project patterns based on task input
await this.pipeline.loadPatterns(userInput);
// Build final packet
const packet = this.pipeline.buildPacket(userInput);
// Add session context
packet.project = context.project || process.env.CURRENT_PROJECT;
packet.user = context.user || 'JC';
return packet;
}
/**
* Build combined system prompt
*/
buildSystemPrompt(route, contextPacket) {
const parts = [
'# System Instructions',
'',
contextPacket.system,
'',
'---',
'',
'# Workflow Mode',
route.systemPrompt,
'',
'---',
'',
'# Current Context',
`Project: ${contextPacket.project || 'None'}`,
`User: ${contextPacket.user || 'Unknown'}`,
'',
'---',
'',
'# Preferences',
contextPacket.preferences.substring(0, 2000) // Truncate for brevity
];
if (contextPacket.memory && contextPacket.memory.trim()) {
parts.push(
'',
'---',
'',
'# Memory',
contextPacket.memory.substring(0, 1000) // Truncate for brevity
);
}
return parts.join('\n');
}
/**
* Validate response against rules
*/
validateResponse(response, workflow) {
const validator = new ResponseValidator(workflow);
return validator.validate(response);
}
/**
* Enforce format compliance
*/
enforceFormat(response, workflow) {
const locker = new FormatLocker(workflow);
return locker.enforceFormat(response, this.autoFix);
}
/**
* Get format prompt for LLM
*/
getFormatPrompt(workflow) {
const locker = new FormatLocker(workflow);
return locker.getFormatPrompt();
}
/**
* Log with verbosity control
*/
log(message) {
if (this.verbose) {
console.log(message);
}
}
/**
* Quick classification without full processing
*/
classify(userInput) {
return this.router.classifyIntent(userInput);
}
/**
* Get workflow config
*/
getWorkflow(name) {
return this.router.getWorkflow(name);
}
}
// Export for use
module.exports = AgentOrchestrator;
// CLI usage
if (require.main === module) {
const userInput = process.argv[2] || 'implement a new feature';
const verbose = process.argv.includes('--verbose') || process.argv.includes('-v');
const orchestrator = new AgentOrchestrator({ verbose: verbose });
orchestrator.process(userInput, { project: process.env.CURRENT_PROJECT })
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error('Error:', err);
process.exit(1);
});
}