Skip to main content
Glama
systempromptio

SystemPrompt Coding Agent

Official
create-task.ts10.5 kB
/** * @fileoverview Create task tool handler for spawning AI agent tasks * @module handlers/tools/orchestrator/create-task * * @remarks * This handler creates new tasks and optionally starts AI agent sessions * to execute them. It supports asynchronous task execution with progress * tracking and comprehensive logging. * * @example * ```typescript * import { handleCreateTask } from './handlers/tools/create-task'; * * const result = await handleCreateTask({ * instructions: "Implement user authentication", * tool: "CLAUDECODE" * }, { sessionId: "mcp_123" }); * ``` */ import type { ToolHandler, ToolHandlerContext, CallToolResult } from "./types.js"; import { formatToolResponse } from "./types.js"; import { logger } from "../../utils/logger.js"; import { CreateTaskArgsSchema, type CreateTaskArgs, ToolNotAvailableError } from "./utils/index.js"; import { validateInput, isToolAvailable, agentOperations, taskOperations } from "./utils/index.js"; import { TASK_STATUS } from "../../constants/task-status.js"; /** * Creates a new task and optionally starts an AI session to execute it * * @param args - Task creation parameters * @param context - Execution context containing session information * @returns Formatted response with task details and session information * * @remarks * This handler: * 1. Validates input arguments * 2. Creates a new task in the task store * 3. Starts an AI agent session (always CLAUDECODE) * 4. Executes initial instructions asynchronously * 5. Returns task details immediately * * @example * ```typescript * const result = await handleCreateTask({ * instructions: "Add JWT-based authentication to the API" * }, { sessionId: "mcp_123" }); * * console.log(result.result.task_id); // Task ID * console.log(result.result.session_id); // Agent session ID * ``` */ export const handleCreateTask: ToolHandler<CreateTaskArgs> = async ( args: unknown, context?: ToolHandlerContext, ): Promise<CallToolResult> => { try { const validated = validateInput(CreateTaskArgsSchema, args); const tool = "CLAUDECODE"; if (!isToolAvailable(tool)) { throw new ToolNotAvailableError(tool); } const projectPath = process.env.PROJECT_ROOT || "/workspace"; // Use title as the task description, limit to 255 characters const truncatedTitle = validated.title.length > 255 ? validated.title.substring(0, 252) + '...' : validated.title; logger.debug("Creating task with title", { title: truncatedTitle, titleLength: truncatedTitle.length, instructionsLength: validated.instructions.length, }); const task = await taskOperations.createTask( { title: truncatedTitle, description: truncatedTitle, tool: tool, projectPath, }, context?.sessionId, ); let agentSessionId: string | null = null; try { await taskOperations.updateTaskStatus(task.id, TASK_STATUS.IN_PROGRESS, context?.sessionId, { completedAt: undefined, }); const agentResult = await agentOperations.startAgentForTask(tool, task, { workingDirectory: projectPath, sessionId: context?.sessionId, }); agentSessionId = agentResult.sessionId; await taskOperations.updateTask(task.id, { assigned_to: agentSessionId }, context?.sessionId); if (validated.instructions && agentSessionId) { executeInitialInstructions( agentSessionId, validated.instructions, task.id, tool, context?.sessionId, ).catch((error) => { logger.error("Background instruction execution failed", { error, taskId: task.id }); taskOperations.addTaskLog( task.id, `Error: ${error}`, context?.sessionId, ); }); await taskOperations.addTaskLog( task.id, `Starting ${tool} agent...`, context?.sessionId, ); } logger.info("Task created successfully", { taskId: task.id, agentSessionId, }); } catch (error) { await taskOperations.updateTaskStatus(task.id, TASK_STATUS.FAILED, context?.sessionId, { error: error instanceof Error ? error.message : String(error), }); if (agentSessionId) { await agentOperations.endAgentSession(agentSessionId, "Task creation failed"); } throw error; } return formatToolResponse({ message: `Task spawned successfully with ID ${task.id}`, result: { task_id: task.id, description: task.description, status: TASK_STATUS.IN_PROGRESS, tool: tool, session_id: agentSessionId, instructions_started: !!validated.instructions, created_at: task.created_at, }, }); } catch (error) { logger.error("Failed to create task", { error, args }); return formatToolResponse({ status: "error", message: error instanceof Error ? error.message : "Failed to create task", error: { type: "task_creation_error", details: error instanceof Error ? error.stack : undefined, }, }); } }; /** * Executes initial instructions with the agent asynchronously * * @param agentSessionId - The agent session ID * @param instructions - Instructions to execute * @param taskId - The task ID * @param tool - The tool being used (always CLAUDECODE) * @param sessionId - Optional MCP session ID * @returns Execution result * * @remarks * This function runs asynchronously after task creation to: * 1. Set up progress handlers for real-time updates * 2. Execute the provided instructions * 3. Log execution results and timing * 4. Update task status based on outcome */ async function executeInitialInstructions( agentSessionId: string, instructions: string, taskId: string, tool: string, sessionId?: string, ): Promise<any> { let cleanup: (() => void) | undefined; try { await taskOperations.addTaskLog( taskId, `Processing request...`, sessionId, ); if (tool === "CLAUDECODE") { cleanup = agentOperations.setupClaudeProgressHandlers( taskId, sessionId || "", agentSessionId, ); } const result = await agentOperations.executeInstructions(agentSessionId, instructions, { taskId, updateProgress: true, }); // Debug logging to trace the result structure logger.info("executeInstructions result", { taskId, success: result.success, hasOutput: !!result.output, outputType: typeof result.output, outputLength: result.output?.length || 0, resultKeys: Object.keys(result || {}), }); const durationSeconds = Math.floor(result.duration / 1000); await taskOperations.addTaskLog( taskId, { timestamp: new Date().toISOString(), level: "info", type: "system", prefix: "EXECUTION_TIME", message: `${tool} execution took ${durationSeconds} seconds`, metadata: { tool, duration: durationSeconds, }, }, sessionId, ); if (result.success) { await taskOperations.addTaskLog( taskId, { timestamp: new Date().toISOString(), level: "info", type: "system", message: "Task completed successfully", metadata: { tool, }, }, sessionId, ); if (result.output) { let parsedOutput: any = null; let isJson = false; try { if (result.output.trim().startsWith("{") || result.output.trim().startsWith("[")) { parsedOutput = JSON.parse(result.output); isJson = true; } } catch (e) { } await taskOperations.addTaskLog( taskId, { timestamp: new Date().toISOString(), level: "info", type: "output", prefix: `${tool}_OUTPUT`, message: isJson ? "Tool execution result" : result.output, metadata: { tool, outputLength: result.output.length, isJson, ...(isJson && parsedOutput ? { data: parsedOutput } : {}), }, }, sessionId, ); } // Add debug logging to trace output capture logger.info("Updating task to WAITING with output", { taskId, hasOutput: !!result.output, outputLength: result.output?.length || 0, }); // Parse the result if it's JSON let parsedResult = result.output || undefined; if (parsedResult && typeof parsedResult === 'string') { try { const trimmed = parsedResult.trim(); if (trimmed.startsWith('{') && trimmed.endsWith('}')) { parsedResult = JSON.parse(trimmed); } } catch (e) { // Keep as string if parsing fails logger.debug('Result is not valid JSON, keeping as string', { error: e }); } } // Only update result if we have one const updateData: any = {}; if (parsedResult !== undefined) { // Structure the result properly updateData.result = { output: typeof parsedResult === 'string' ? parsedResult : JSON.stringify(parsedResult, null, 2), success: true, data: typeof parsedResult === 'object' ? parsedResult : undefined }; } await taskOperations.updateTaskStatus(taskId, TASK_STATUS.WAITING, sessionId, updateData); } else { await taskOperations.updateTaskStatus(taskId, TASK_STATUS.FAILED, sessionId, { error: result.error, }); } return result; } catch (error) { logger.error("Failed to execute instructions", { error, taskId }); await taskOperations.addTaskLog( taskId, { timestamp: new Date().toISOString(), level: "error", type: "system", prefix: "ERROR", message: `Instructions error: ${error}`, metadata: { error: error instanceof Error ? { name: error.name, message: error.message, stack: error.stack, } : error, }, }, sessionId, ); throw error; } finally { if (cleanup) { cleanup(); } } }

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