agentic-qe
Version:
Agentic Quality Engineering Fleet System - AI-driven quality management platform
298 lines • 9.87 kB
JavaScript
"use strict";
/**
* Agent - Base class for all autonomous agents in the AQE Fleet
*
* @remarks
* The Agent class provides the foundational capabilities for autonomous agents
* including lifecycle management, task execution, capability advertisement,
* and performance metrics tracking.
*
* All concrete agent implementations must extend this class and implement
* the abstract methods for agent-specific behavior.
*
* @example
* ```typescript
* class CustomAgent extends Agent {
* protected async onInitialize(): Promise<void> {
* // Custom initialization logic
* }
*
* protected async executeTaskLogic(task: Task): Promise<any> {
* // Custom task execution logic
* return { success: true };
* }
* }
* ```
*
* @public
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Agent = exports.AgentStatus = void 0;
const events_1 = require("events");
const Task_1 = require("./Task");
const Logger_1 = require("../utils/Logger");
/**
* Operational status of an agent
*
* @public
*/
var AgentStatus;
(function (AgentStatus) {
/** Agent is being initialized */
AgentStatus["INITIALIZING"] = "initializing";
/** Agent is idle and ready to accept tasks */
AgentStatus["IDLE"] = "idle";
/** Agent is active and monitoring for tasks */
AgentStatus["ACTIVE"] = "active";
/** Agent is currently executing a task */
AgentStatus["BUSY"] = "busy";
/** Agent encountered an error */
AgentStatus["ERROR"] = "error";
/** Agent is in the process of stopping */
AgentStatus["STOPPING"] = "stopping";
/** Agent has been stopped */
AgentStatus["STOPPED"] = "stopped";
})(AgentStatus || (exports.AgentStatus = AgentStatus = {}));
class Agent extends events_1.EventEmitter {
constructor(id, type, config, eventBus) {
super();
this.currentTask = null;
this.capabilities = [];
this.startTime = null;
this.id = id;
this.type = type;
this.config = config;
this.eventBus = eventBus;
this.logger = Logger_1.Logger.getInstance();
this.status = AgentStatus.INITIALIZING;
this.metrics = {
tasksCompleted: 0,
tasksFailured: 0,
averageExecutionTime: 0,
uptime: 0,
lastActivity: new Date()
};
this.setupEventHandlers();
}
/**
* Initialize the agent and its capabilities
*
* @remarks
* Performs initialization sequence including capability setup and
* agent-specific initialization logic.
*
* @returns A promise that resolves when initialization is complete
* @throws {Error} If initialization fails
* @fires agent:initialized
*
* @public
*/
async initialize() {
this.logger.info(`Initializing agent ${this.type} (${this.id})`);
try {
// Initialize agent-specific capabilities
await this.initializeCapabilities();
// Run agent-specific initialization
await this.onInitialize();
this.status = AgentStatus.IDLE;
this.startTime = new Date();
this.logger.info(`Agent ${this.type} (${this.id}) initialized successfully`);
this.emit('agent:initialized', { agentId: this.id, type: this.type });
}
catch (error) {
this.status = AgentStatus.ERROR;
this.logger.error(`Failed to initialize agent ${this.id}:`, error);
this.emit('agent:error', { agentId: this.id, error });
throw error;
}
}
/**
* Start the agent
*/
async start() {
if (this.status !== AgentStatus.IDLE) {
throw new Error(`Agent ${this.id} must be idle to start`);
}
this.status = AgentStatus.ACTIVE;
await this.onStart();
this.logger.info(`Agent ${this.type} (${this.id}) started`);
this.emit('agent:started', { agentId: this.id });
}
/**
* Stop the agent
*/
async stop() {
this.status = AgentStatus.STOPPING;
// Wait for current task to complete if any
if (this.currentTask && this.currentTask.getStatus() === Task_1.TaskStatus.RUNNING) {
await this.currentTask.waitForCompletion();
}
await this.onStop();
this.status = AgentStatus.STOPPED;
this.logger.info(`Agent ${this.type} (${this.id}) stopped`);
this.emit('agent:stopped', { agentId: this.id });
}
/**
* Assign a task to this agent for execution
*
* @param task - The task to assign
* @returns A promise that resolves when the task is assigned
* @throws {Error} If agent is not available or cannot handle the task type
* @fires task:assigned
*
* @public
*/
async assignTask(task) {
// Check if agent already has a task first (more specific error)
if (this.currentTask) {
throw new Error(`Agent ${this.id} already has an assigned task`);
}
// Then check if agent is in correct status
if (this.status !== AgentStatus.ACTIVE && this.status !== AgentStatus.IDLE && this.status !== AgentStatus.BUSY) {
throw new Error(`Agent ${this.id} is not available for task assignment`);
}
if (!this.canHandleTaskType(task.getType())) {
throw new Error(`Agent ${this.id} cannot handle task type ${task.getType()}`);
}
this.currentTask = task;
this.status = AgentStatus.BUSY; // Set BUSY immediately to prevent race conditions
this.metrics.lastActivity = new Date();
this.logger.info(`Task ${task.getId()} assigned to agent ${this.id}`);
this.emit('task:assigned', { agentId: this.id, taskId: task.getId() });
// Execute the task asynchronously but don't await it
this.executeTask(task).catch(error => {
this.logger.error(`Unhandled error in task execution for ${task.getId()}:`, error);
});
}
/**
* Execute a task
*/
async executeTask(task) {
const startTime = Date.now();
try {
// Status already set to BUSY in assignTask
task.setStatus(Task_1.TaskStatus.RUNNING);
this.eventBus.emit('task:started', { agentId: this.id, taskId: task.getId() });
// Execute agent-specific task logic
const result = await this.executeTaskLogic(task);
task.setResult(result);
task.setStatus(Task_1.TaskStatus.COMPLETED);
// Update metrics
const executionTime = Date.now() - startTime;
this.updateMetrics(executionTime, true);
this.logger.info(`Task ${task.getId()} completed by agent ${this.id}`);
this.eventBus.emit('task:completed', {
agentId: this.id,
taskId: task.getId(),
result,
executionTime
});
}
catch (error) {
task.setError(error);
task.setStatus(Task_1.TaskStatus.FAILED);
// Update metrics
const executionTime = Date.now() - startTime;
this.updateMetrics(executionTime, false);
this.logger.error(`Task ${task.getId()} failed in agent ${this.id}:`, error);
this.eventBus.emit('task:failed', {
agentId: this.id,
taskId: task.getId(),
error,
executionTime
});
}
finally {
this.currentTask = null;
this.status = AgentStatus.ACTIVE;
}
}
/**
* Check if agent can handle a specific task type
*
* @param taskType - The task type to check
* @returns True if the agent has a capability for this task type
*
* @public
*/
canHandleTaskType(taskType) {
return this.capabilities.some(capability => capability.taskTypes.includes(taskType));
}
/**
* Get agent ID
*/
getId() {
return this.id;
}
/**
* Get agent type
*/
getType() {
return this.type;
}
/**
* Get agent status
*/
getStatus() {
return this.status;
}
/**
* Get agent capabilities
*/
getCapabilities() {
return this.capabilities;
}
/**
* Get agent performance metrics
*
* @returns Current metrics including task counts and execution times
*
* @public
*/
getMetrics() {
const uptime = this.startTime ? Date.now() - this.startTime.getTime() : 0;
return {
...this.metrics,
uptime
};
}
/**
* Get current task
*/
getCurrentTask() {
return this.currentTask;
}
/**
* Update agent metrics
*/
updateMetrics(executionTime, success) {
if (success) {
this.metrics.tasksCompleted++;
}
else {
this.metrics.tasksFailured++;
}
// Update average execution time
const totalTasks = this.metrics.tasksCompleted + this.metrics.tasksFailured;
if (totalTasks > 0) {
this.metrics.averageExecutionTime =
(this.metrics.averageExecutionTime * (totalTasks - 1) + executionTime) / totalTasks;
}
else {
this.metrics.averageExecutionTime = executionTime;
}
this.metrics.lastActivity = new Date();
}
/**
* Setup event handlers
*/
setupEventHandlers() {
this.on('error', (error) => {
this.status = AgentStatus.ERROR;
this.logger.error(`Agent ${this.id} encountered an error:`, error);
this.eventBus.emit('agent:error', { agentId: this.id, error });
});
}
}
exports.Agent = Agent;
//# sourceMappingURL=Agent.js.map