Skip to main content
Glama
systempromptio

SystemPrompt Coding Agent

Official
agent.ts10.4 kB
/** * @fileoverview Agent management utilities for orchestrator tools providing * high-level operations for starting, executing, and managing AI agent sessions * @module handlers/tools/orchestrator/utils/agent */ import { AgentManager } from "../../../services/agent-manager/index.js"; import { ClaudeCodeService, ClaudeCodeOptions } from "../../../services/claude-code/index.js"; import { TaskStore } from "../../../services/task-store.js"; import { logger } from "../../../utils/logger.js"; import type { AITool, Task, TaskLogEntry } from "../../../types/task.js"; import { TASK_STATUS } from "../../../constants/task-status.js"; /** * Result of starting an agent session */ export interface AgentStartResult { sessionId: string; serviceSessionId?: string; tool: AITool; } /** * Result of executing agent instructions */ export interface AgentExecuteResult { success: boolean; output?: string; error?: string; duration: number; } /** * High-level utilities for agent management */ export class AgentOperations { public readonly agentManager: AgentManager; private readonly claudeService: ClaudeCodeService; private readonly taskStore: TaskStore; constructor(agentManager?: AgentManager, taskStore?: TaskStore) { this.agentManager = agentManager || AgentManager.getInstance(); this.claudeService = ClaudeCodeService.getInstance(); this.taskStore = taskStore || TaskStore.getInstance(); } /** * Starts an agent session for a task * * @param tool The AI tool to use * @param task The task to execute * @param options Additional options * @returns Agent session information */ async startAgentForTask( tool: AITool, task: Task, options: { workingDirectory?: string; sessionId?: string; } = {}, ): Promise<AgentStartResult> { const workingDirectory = options.workingDirectory || process.cwd(); try { if (tool === "CLAUDECODE") { const claudeOptions: ClaudeCodeOptions = { workingDirectory, }; const sessionId = await this.agentManager.startClaudeSession({ project_path: workingDirectory, task_id: task.id, mcp_session_id: options.sessionId, options: claudeOptions, }); const agentSession = this.agentManager.getSession(sessionId); if (agentSession?.serviceSessionId) { this.claudeService.setTaskId(agentSession.serviceSessionId, task.id); if (options.sessionId) { this.claudeService.setMcpSessionId(agentSession.serviceSessionId, options.sessionId); const logEntry: TaskLogEntry = { timestamp: new Date().toISOString(), level: 'info', type: 'system', message: `Session ready`, metadata: { sessionId: agentSession.serviceSessionId, mcpSessionId: options.sessionId, } }; await this.taskStore.addLog(task.id, logEntry, options.sessionId); } } return { sessionId, serviceSessionId: agentSession?.serviceSessionId, tool: "CLAUDECODE", }; } else { throw new Error(`Unsupported tool: ${tool}`); } } catch (error) { logger.error("Failed to start agent", { tool, taskId: task.id, error }); throw error; } } /** * Executes instructions with an agent * * @param sessionId The agent session ID * @param instructions The instructions to execute * @param options Execution options * @returns Execution result */ async executeInstructions( sessionId: string, instructions: string, options: { taskId?: string; updateProgress?: boolean; timeout?: number; } = {}, ): Promise<AgentExecuteResult> { const startTime = Date.now(); const { taskId, updateProgress = true, timeout = 300000 } = options; try { let progressInterval: NodeJS.Timeout | undefined; if (updateProgress && taskId) { await this.taskStore.addLog(taskId, { timestamp: new Date().toISOString(), level: "info", type: "progress", message: "Task started, processing instructions..." }); let updateCount = 0; progressInterval = setInterval(async () => { updateCount++; const elapsed = Math.floor((Date.now() - startTime) / 1000); const minutes = Math.floor(elapsed / 60); const seconds = elapsed % 60; let message = ""; if (minutes > 0) { message = `Still processing... (${minutes}m ${seconds}s)`; } else { message = `Processing... (${elapsed}s)`; } if (elapsed > 120) { message = `Complex task in progress... (${minutes}m ${seconds}s)`; } await this.taskStore.addLog(taskId, { timestamp: new Date().toISOString(), level: "info", type: "progress", message: message }); }, 30000); } try { const result = await this.agentManager.sendCommand(sessionId, { command: instructions, timeout, }); const duration = Date.now() - startTime; if (progressInterval) { clearInterval(progressInterval); } if (result.success) { return { success: true, output: result.output, duration, }; } else { return { success: false, error: typeof result.error === "string" ? result.error : result.error?.message || "Command failed", duration, }; } } finally { if (progressInterval) { clearInterval(progressInterval); } } } catch (error) { const duration = Date.now() - startTime; logger.error("Failed to execute instructions", { sessionId, error }); return { success: false, error: error instanceof Error ? error.message : String(error), duration, }; } } /** * Sets up event handlers for Claude progress tracking * * @param taskId The task ID to track * @param sessionId The session ID * @param agentSessionId The agent session ID * @returns Cleanup function to remove handlers */ setupClaudeProgressHandlers( taskId: string, sessionId?: string, agentSessionId?: string, ): () => void { const progressHandler = async (data: any) => { if (data.taskId === taskId) { await this.taskStore.addLog(taskId, `[PROGRESS] ${data.event}: ${data.data}`, sessionId); if (data.event === "error:occurred") { await this.taskStore.updateTask(taskId, { status: TASK_STATUS.FAILED }, sessionId); } } }; const streamHandler = async (data: any) => { if (data.taskId === taskId && agentSessionId) { const agentSession = this.agentManager.getSession(agentSessionId); if (agentSession) { agentSession.last_activity = new Date().toISOString(); } } }; this.claudeService.on("task:progress", progressHandler); this.claudeService.on("stream:data", streamHandler); return () => { this.claudeService.off("task:progress", progressHandler); this.claudeService.off("stream:data", streamHandler); }; } /** * Ends an agent session gracefully * * @param sessionId The session ID to end * @param reason Optional reason for ending */ async endAgentSession(sessionId: string, reason?: string): Promise<void> { try { const session = this.agentManager.getSession(sessionId); if (!session) { logger.warn("Session not found when ending", { sessionId }); return; } await this.agentManager.endSession(sessionId); if (reason) { logger.info("Agent session ended", { sessionId, reason }); } } catch (error) { logger.error("Failed to end agent session", { sessionId, error }); } } /** * Gets statistics about active agents * * @returns Agent statistics */ async getAgentStats(): Promise<{ totalSessions: number; activeSessions: number; byTool: Record<string, number>; byStatus: Record<string, number>; }> { const sessions = this.agentManager.getAllSessions(); const byTool = sessions.reduce( (acc) => { const tool = "CLAUDECODE"; acc[tool] = (acc[tool] || 0) + 1; return acc; }, {} as Record<string, number>, ); const byStatus = sessions.reduce( (acc, session) => { acc[session.status] = (acc[session.status] || 0) + 1; return acc; }, {} as Record<string, number>, ); return { totalSessions: sessions.length, activeSessions: sessions.filter((s) => s.status === "active" || s.status === "busy").length, byTool, byStatus, }; } } /** * Singleton instance of agent operations - initialized lazily to avoid circular dependencies */ let _agentOperations: AgentOperations | null = null; /** * Export singleton with lazy initialization */ export const agentOperations = { get instance(): AgentOperations { if (!_agentOperations) { _agentOperations = new AgentOperations(); } return _agentOperations; }, // Direct property access get agentManager() { return this.instance.agentManager; }, // Method delegations startAgentForTask: (...args: Parameters<AgentOperations['startAgentForTask']>) => agentOperations.instance.startAgentForTask(...args), executeInstructions: (...args: Parameters<AgentOperations['executeInstructions']>) => agentOperations.instance.executeInstructions(...args), setupClaudeProgressHandlers: (...args: Parameters<AgentOperations['setupClaudeProgressHandlers']>) => agentOperations.instance.setupClaudeProgressHandlers(...args), endAgentSession: (...args: Parameters<AgentOperations['endAgentSession']>) => agentOperations.instance.endAgentSession(...args), getAgentStats: (...args: Parameters<AgentOperations['getAgentStats']>) => agentOperations.instance.getAgentStats(...args), };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systempromptio/systemprompt-code-orchestrator'

If you have feedback or need assistance with the MCP directory API, please join our Discord server