/** * Smoke test for Ruflo-inspired systems * Exercises: PluginManager, PluginLoader, HookManager, Agent, Task, SwarmCoordinator, Memory */ let passed = 0, failed = 0; const assert = (msg, cond) => { if (cond) { passed++; } else { failed++; console.error(`❌ ${msg}`); } }; // ── 1. Plugin System ── console.log('\n🧩 Plugin System'); const { PluginManager, PLUGIN_STATES } = await import('./src/plugins/PluginManager.js'); const { PluginLoader } = await import('./src/plugins/PluginLoader.js'); const { BasePlugin } = await import('./src/plugins/Plugin.js'); const { EXTENSION_POINTS } = await import('./src/plugins/ExtensionPoints.js'); const pm = new PluginManager({ coreVersion: '3.0.0' }); await pm.initialize(); assert('PluginManager initializes', pm.isInitialized() === true); // Register a test plugin class TestPlugin extends BasePlugin { constructor() { super({ id: 'test-plugin', name: 'Test Plugin', version: '1.0.0' }); } async _onInitialize() { this._loaded = true; } async _onShutdown() { this._unloaded = true; } } const testPlugin = new TestPlugin(); await pm.loadPlugin(testPlugin); assert('Plugin loaded', pm.getPlugin('test-plugin')?.id === 'test-plugin'); // Register an extension point on the plugin testPlugin.registerExtensionPoint('pre_tool', async (ctx) => ({ handled: true, toolName: ctx.toolName })); assert('Plugin registers extension points', testPlugin.getExtensionPoints().length > 0); // Invoke extension point const results = await pm.invokeExtensionPoint(EXTENSION_POINTS.PRE_TOOL, { toolName: 'test' }); assert('Extension point invocation returns array', Array.isArray(results)); // PluginLoader const loader = new PluginLoader(pm); assert('PluginLoader created', loader._manager === pm); // Load with loader const testPlugin2 = new TestPlugin(); testPlugin2.id = 'test-plugin-2'; testPlugin2.name = 'Test Plugin 2'; await loader.loadPlugin(testPlugin2); assert('Loader loads plugin', pm.getPlugin('test-plugin-2')?.id === 'test-plugin-2'); // Load multiple const p3 = new (class extends BasePlugin { constructor() { super({ id: 'test-plugin-3', name: 'Test Plugin 3', version: '1.0.0' }); } })(); await loader.loadPlugins([p3]); assert('Loader loads multiple plugins', pm.getPluginCount() >= 3); // Unload await pm.unloadPlugin('test-plugin'); assert('Plugin unloaded', pm.getPlugin('test-plugin') === null); // ── 2. Hook System ── console.log('\n🔗 Hook System'); const { hookManager, HOOK_TYPES, HookManager } = await import('./src/bot/hooks.js'); let preToolFired = false; hookManager.register(HOOK_TYPES.PRE_TOOL, 'test-hook', async (ctx) => { preToolFired = true; return true; }); assert('Hook registered', hookManager._hooks.get(HOOK_TYPES.PRE_TOOL)?.length > 0); const hookCtx = { toolName: 'bash', args: { command: 'echo hi' } }; await hookManager.execute(HOOK_TYPES.PRE_TOOL, hookCtx); assert('Pre-tool hook fires', preToolFired); // Maintenance assert('Hook registered in map', hookManager._hooks.has(HOOK_TYPES.PRE_TOOL)); // ── 3. Agent System ── console.log('\n🤖 Agent System'); const { Agent } = await import('./src/agents/Agent.js'); const { Task, TASK_PRIORITIES, TASK_STATUSES } = await import('./src/agents/Task.js'); const { SwarmCoordinator } = await import('./src/agents/SwarmCoordinator.js'); const { initAgents, AgentOrchestrator } = await import('./src/agents/index.js'); // Agent creation const coder = new Agent({ id: 'coder-1', type: 'coder', name: 'Coder Alpha', capabilities: ['code', 'refactor'] }); assert('Agent created with id', coder.id === 'coder-1'); assert('Agent type set', coder.type === 'coder'); assert('Agent capabilities stored', coder.capabilities.length === 2); assert('Agent starts idle', coder.status === 'idle'); assert('Agent idle getter', coder.idle === true); // Agent canHandleTask const codeTask = { requiredCapabilities: ['code'] }; const reviewTask = { requiredCapabilities: ['review'] }; assert('Agent can handle matching task', coder.canHandleTask(codeTask)); assert('Agent cannot handle mismatched task', coder.canHandleTask(reviewTask) === false); assert('Agent has capability', coder.hasCapability('code')); // Task creation // Task creation const task1 = new Task({ id: 'task-1', type: 'code', description: 'Write parser', priority: TASK_PRIORITIES.HIGH }); assert('Task created with id', task1.id === 'task-1'); assert('Task priority high', task1.priority === TASK_PRIORITIES.HIGH); const task2 = new Task({ id: 'task-2', type: 'review', description: 'Review parser', priority: TASK_PRIORITIES.NORMAL, dependencies: ['task-1'] }); assert('Task dependencies', task2.dependencies.length === 1); // Task status transitions task1.start(); assert('Task started, status in_progress', task1.status === TASK_STATUSES.IN_PROGRESS); task1.complete({ output: 'parser written' }); assert('Task completed', task1.status === TASK_STATUSES.COMPLETED); assert('Task result stored', task1._result?.output === 'parser written'); // Task fail task2.start(); task2.fail({ message: 'design review needed' }); assert('Task failed', task2.status === TASK_STATUSES.FAILED); assert('Task error stored', task2.error?.includes('design review')); // ── 4. Swarm Coordinator ── console.log('\n🌐 Swarm Coordinator'); const swarm = new SwarmCoordinator({ topology: 'simple', maxAgents: 5 }); await swarm.initialize(); assert('Swarm initialized', swarm.initialized === true); // Spawn an agent const agent = await swarm.spawnAgent({ type: 'coder', name: 'Swarm Coder', capabilities: ['code'] }); assert('Swarm agent spawned', agent.id?.startsWith('agent_')); assert('Swarm agent type', agent.type === 'coder'); // Spawn another const reviewer = await swarm.spawnAgent({ type: 'reviewer', name: 'Swarm Reviewer' }); assert('Second agent spawned', reviewer.id !== agent.id); // Execute a task const execTask = new Task({ type: 'code', description: 'Write tests', priority: TASK_PRIORITIES.HIGH }); const execResult1 = await swarm.executeTask(agent.id, execTask); assert('Swarm task executed', execResult1 !== undefined); // Distribute tasks const distResult = await swarm.distributeTasks([ new Task({ type: 'code', description: 'Feature X', priority: TASK_PRIORITIES.HIGH, assignedTo: agent.id }), new Task({ type: 'review', description: 'Review X', priority: TASK_PRIORITIES.NORMAL, assignedTo: reviewer.id }), ]); assert('Swarm distribute returns array', Array.isArray(distResult)); // Swarm state const state = swarm.getSwarmState(); assert('Swarm state has topology', state.topology === 'simple'); assert('Swarm state has agents count', state.agents > 0); assert('Swarm state has byStatus', typeof state.byStatus === 'object'); // Terminate agent await swarm.terminateAgent(agent.id); const stateAfter = swarm.getSwarmState(); assert('Agent terminated reduces count', stateAfter.agents === state.agents - 1); // Shutdown await swarm.shutdown(); assert('Swarm shutdown resets initialized', swarm.initialized === false); // ── 5. Agent Orchestrator ── console.log('\n🎭 Agent Orchestrator'); const agentsFromInit = await initAgents(); assert('initAgents returns array', Array.isArray(agentsFromInit)); assert('initAgents has agents', agentsFromInit.length > 0); const orchestra = new AgentOrchestrator(agentsFromInit, { topology: 'simple', maxAgents: 10 }); await orchestra.swarm.initialize(); assert('Orchestrator created', orchestra.agentDefs.length > 0); assert('Orchestrator has agentMap', orchestra.agentMap.size > 0); // Execute a task with an agent const execResult = await orchestra.execute('coder', 'Write a parser'); assert('Orchestrator execute returns result', execResult.success === true); assert('Orchestrator execute returns agent name', typeof execResult.agent === 'string'); // Multi-agent execution const multiResult = await orchestra.executeMultiAgent([ { agentId: 'coder', description: 'Write tests' }, { agentId: 'reviewer', description: 'Review code' }, ]); assert('Multi-agent execution returns array', Array.isArray(multiResult)); assert('Multi-agent execution has results', multiResult.length > 0); assert('Multi-agent execution results have taskIds', multiResult[0].taskId); // ── 6. Memory Backend ── // Memory Backend const { JSONBackend, InMemoryBackend, MEMORY_TYPES } = await import('./src/bot/memory-backend.js'); const fs = await import('fs'); const os = await import('os'); const path = await import('path'); const memPath = path.join(os.tmpdir(), `zcode-mem-test-${Date.now()}.json`); const jmem = new JSONBackend(memPath, 100); await jmem.initialize(); console.log('DEBUG: jmem._loaded =', jmem._loaded); console.log('DEBUG: jmem._entries.size =', jmem._entries.size); assert('JSONBackend initializes', jmem._loaded === true); await jmem.store({ type: MEMORY_TYPES.FACT, key: 'language', value: 'JavaScript' }); await jmem.store({ type: MEMORY_TYPES.LESSON, key: 'language', value: 'JavaScript' }); const retrieved = await jmem.retrieve('language'); console.log('DEBUG: retrieved =', retrieved); assert('JSONBackend stores and retrieves fact', retrieved?.value === 'JavaScript'); await jmem.store({ type: MEMORY_TYPES.PATTERN, key: 'naming', description: 'camelCase for vars' }); const all = jmem.getAll(); assert('JSONBackend getAll returns object', typeof all === 'object'); assert('JSONBackend has lesson', all.lesson?.length >= 1); // InMemoryBackend const imem = new InMemoryBackend(50, 5000); // 5 second TTL console.log('DEBUG: InMemoryBackend created, count =', imem.getCount()); await imem.store({ id: 'session', data: 'test' }); console.log('DEBUG: after store, count =', imem.getCount()); const session = await imem.retrieve('session'); console.log('DEBUG: session =', session); assert('InMemoryBackend stores and retrieves', session?.data === 'test'); // Wait for TTL to expire await new Promise(r => setTimeout(r, 100)); const count = imem.getCount(); assert('InMemoryBackend has count', count >= 0); // ── RESULTS ── console.log(`\n${'═'.repeat(50)}`); console.log(`📊 RESULTS: ${passed} passed, ${failed} failed out of ${passed + failed} assertions`); if (failed > 0) process.exit(1); console.log('✅ ALL SMOKE TESTS PASSED');