claude-flow-novice
Version:
Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes Local RuVector Accelerator and all CFN skills for complete functionality.
297 lines (296 loc) • 9.5 kB
JavaScript
/**
* Task Mode Adapter - In-Memory and Hybrid Coordination
*
* Provides task mode coordination that works with or without trigger.dev:
* - In-memory event queuing when trigger.dev unavailable
* - Hybrid mode using trigger.dev webhooks for event notifications
* - Fallback to memory when API calls fail
*
* Configuration:
* - TASK_MODE_USE_MEMORY: Force memory mode (default: auto-detect)
* - TASK_MODE_TIMEOUT_MS: Default timeout for agent completion waits
*/ import { EventEmitter } from 'node:events';
import TriggerDevClient from './trigger-dev-client.js';
/**
* Task mode event storage (FIFO queue)
*/ export class TaskModeEventQueue {
queue = [];
eventEmitter = new EventEmitter();
/**
* Enqueue event for later processing
*/ enqueue(event) {
this.queue.push(event);
this.eventEmitter.emit('event-added', event);
}
/**
* Dequeue next event (FIFO)
*/ dequeue() {
return this.queue.shift();
}
/**
* Get all events of specific type
*/ getByType(type) {
return this.queue.filter((e)=>e.type === type);
}
/**
* Get all events for task
*/ getByTaskId(taskId) {
return this.queue.filter((e)=>e.taskId === taskId);
}
/**
* Clear all events
*/ clear() {
this.queue = [];
}
/**
* Get queue size
*/ size() {
return this.queue.length;
}
/**
* Subscribe to new events
*/ on(event, listener) {
this.eventEmitter.on(event, listener);
}
/**
* Unsubscribe from events
*/ off(event, listener) {
this.eventEmitter.off(event, listener);
}
}
/**
* TaskModeCoordinator - In-memory and hybrid coordination
*
* Provides task mode without Redis/external coordination:
* - Spawns agents with in-memory tracking
* - Waits for completion via event listeners
* - Falls back to memory if trigger.dev unavailable
* - Optionally uses trigger.dev for webhook notifications
*/ export class TaskModeCoordinator {
config;
triggerDevClient;
eventQueue = new TaskModeEventQueue();
pendingCompletions = new Map();
constructor(options){
this.config = {
useMemory: options?.useMemory !== undefined ? options.useMemory : process.env.TASK_MODE_USE_MEMORY === 'true' || !process.env.TRIGGER_API_URL,
defaultTimeoutMs: options?.defaultTimeoutMs || 30000,
useTriggerDev: Boolean(process.env.TRIGGER_API_URL)
};
// Initialize trigger.dev client if configured
if (this.config.useTriggerDev && !this.config.useMemory) {
try {
this.triggerDevClient = new TriggerDevClient(options?.triggerDevConfig);
} catch (error) {
// Fall back to memory mode if trigger.dev config fails
console.warn('Trigger.dev initialization failed, using memory mode:', error);
this.config.useMemory = true;
}
}
}
/**
* Spawn agent and wait for completion
*
* TODO: RUNTIME_TEST: Verify agent spawning works in memory mode
* TODO: RUNTIME_TEST: Test timeout behavior with slow agents
*
* @param agentType Type of agent to spawn
* @param taskId Task ID
* @param payload Agent configuration
* @param timeoutMs Optional timeout override
* @returns Promise resolving to agent result
* @throws Error on spawn failure or timeout
*/ async spawnAgent(agentType, taskId, payload, timeoutMs) {
const agentId = this.generateAgentId(agentType);
const timeout = timeoutMs || this.config.defaultTimeoutMs;
try {
// If using trigger.dev, trigger the run
if (this.triggerDevClient && !this.config.useMemory) {
await this.triggerViaTriggerDev(agentType, taskId, payload);
}
// Wait for completion either via webhook or event queue
const result = await this.waitForCompletion(agentId, timeout);
return result;
} catch (error) {
if (error instanceof Error) {
throw error;
}
throw new Error(`Agent spawn failed: ${String(error)}`);
}
}
/**
* Register agent completion event
*
* Used by webhook handlers to signal completion
*/ registerAgentComplete(payload) {
this.eventQueue.enqueue({
type: 'agent-complete',
taskId: payload.taskId,
payload,
timestamp: Date.now()
});
// Resolve pending completion if waiting
const pending = this.pendingCompletions.get(payload.agentId);
if (pending) {
clearTimeout(pending.timeoutHandle);
pending.resolve({
agentId: payload.agentId,
agentType: payload.agentType,
success: payload.status === 'success',
output: payload.output,
confidenceScore: payload.confidenceScore,
executionTimeMs: payload.executionTimeMs
});
this.pendingCompletions.delete(payload.agentId);
}
return {
success: true,
message: 'Agent completion registered',
data: {
agentId: payload.agentId
}
};
}
/**
* Register gate result
*/ registerGateResult(payload) {
this.eventQueue.enqueue({
type: 'gate-result',
taskId: payload.taskId,
payload,
timestamp: Date.now()
});
return {
success: true,
message: 'Gate result registered',
data: {
taskId: payload.taskId,
passed: payload.passed
}
};
}
/**
* Register consensus result
*/ registerConsensusResult(payload) {
this.eventQueue.enqueue({
type: 'consensus-result',
taskId: payload.taskId,
payload,
timestamp: Date.now()
});
return {
success: true,
message: 'Consensus result registered',
data: {
taskId: payload.taskId,
consensusScore: payload.consensusScore
}
};
}
/**
* Register PO decision
*/ registerPODecision(payload) {
this.eventQueue.enqueue({
type: 'po-decision',
taskId: payload.taskId,
payload,
timestamp: Date.now()
});
return {
success: true,
message: 'PO decision registered',
data: {
taskId: payload.taskId,
decision: payload.decision
}
};
}
/**
* Get event queue (for testing and inspection)
*/ getEventQueue() {
return this.eventQueue;
}
/**
* Get all events for task
*/ getTaskEvents(taskId) {
return this.eventQueue.getByTaskId(taskId);
}
/**
* Check if using memory mode
*/ isMemoryMode() {
return this.config.useMemory;
}
/**
* Internal: Trigger agent run via trigger.dev
*
* @private
*/ async triggerViaTriggerDev(agentType, taskId, payload) {
if (!this.triggerDevClient) {
return;
}
try {
// TODO: RUNTIME_TEST: Verify trigger.dev API call succeeds
await this.triggerDevClient.triggerCFNLoop({
taskId,
description: `Task mode agent: ${agentType}`,
mode: 'standard',
successCriteria: {
gate: 'test-pass-rate >= 0.95',
consensusThreshold: 0.9,
testPassRateThreshold: 0.95
},
context: {
agentType,
payload
}
});
} catch (error) {
// Fall back to memory mode if trigger.dev fails
console.warn('Trigger.dev trigger failed, continuing in memory mode:', error);
}
}
/**
* Internal: Wait for agent completion
*
* @private
*/ waitForCompletion(agentId, timeoutMs) {
return new Promise((resolve, reject)=>{
const timeoutHandle = setTimeout(()=>{
this.pendingCompletions.delete(agentId);
reject(new Error(`Agent ${agentId} did not complete within ${timeoutMs}ms`));
}, timeoutMs);
this.pendingCompletions.set(agentId, {
agentId,
resolve,
reject,
timeoutHandle
});
});
}
/**
* Internal: Generate unique agent ID
*
* @private
*/ generateAgentId(agentType) {
return `${agentType}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
/**
* Create task mode coordinator singleton
*/ let coordinatorInstance = null;
export const createTaskModeCoordinator = (options)=>{
if (!coordinatorInstance) {
coordinatorInstance = new TaskModeCoordinator(options);
}
return coordinatorInstance;
};
/**
* Get existing coordinator instance or create new one
*/ export const getTaskModeCoordinator = ()=>{
if (!coordinatorInstance) {
coordinatorInstance = new TaskModeCoordinator();
}
return coordinatorInstance;
};
export default TaskModeCoordinator;
//# sourceMappingURL=task-mode-adapter.js.map