UNPKG

taskforce-aiagent

Version:

TaskForce is a modular, open-source, production-ready TypeScript agent framework for orchestrating AI agents, LLM-powered autonomous agents, task pipelines, dynamic toolchains, RAG workflows and memory/retrieval systems.

217 lines (189 loc) โ€ข 9.38 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SmartManagerAgent = void 0; const agent_js_1 = require("./agent.js"); const aiClient_js_1 = require("../llm/aiClient.js"); const task_schema_js_1 = require("../tasks/task.schema.js"); const log_helper_js_1 = require("../helpers/log.helper.js"); const chalk_1 = __importDefault(require("chalk")); const helper_js_1 = require("../helpers/helper.js"); class SmartManagerAgent extends agent_js_1.Agent { constructor() { super(...arguments); this.taskEvaluationHistory = new Map(); } async planTasks(tasks, context) { const prompt = `User input: ${JSON.stringify(context, null, 2)} Which of the following tasks should be executed, and in what order? ${tasks .map((t, i) => `${i + 1}. ${t.id}: ${t.name} => ${t.description}`) .join("\n")} You will return a JSON object that follows this JSON schema: ${JSON.stringify(task_schema_js_1.TaskOrderJsonSchema, null, 2)} ONLY return a valid JSON object matching the schema above. Do not wrap in markdown. Do not include explanation, comments, or extra keys.`; const raw = await (0, aiClient_js_1.callAIModel)("Manager Agent", this.model, [ { role: "system", content: "You are an intelligent project manager." }, { role: "user", content: prompt }, ], this.getVerbose()); (0, log_helper_js_1.TFLog)(`[Manager Agent] Planning tasks with context:\n${JSON.stringify(context, null, 2)}`, chalk_1.default.green); (0, log_helper_js_1.TFLog)(`[Manager Agent] Model returned task order:\n${raw}`, chalk_1.default.green); const result = task_schema_js_1.TaskOrderSchema.parse(JSON.parse(raw)); return result.tasks .map((id) => tasks.find((t) => t.id === id)) .filter((t) => !!t); } async decomposeTask(mainTask, subAgents, verbose) { const prompt = ` You are a task decomposition AI. Given the task below, break it down into a list of ordered subtasks with 'id', 'name', 'description', and assign each subtask to one of the agents provided. Task: ${mainTask.description} Available agents: ${subAgents.map((a) => `- ${a.name}: ${a.role} / ${a.goal}`).join("\n")} Each agent is an expert in their role. Assign tasks considering their expertise. Return only a valid JSON array, no markdown, no explanations. Example: [ { "id": "collect_data", "name": "Collect data", "description": "Gather all relevant data", "agent": "Data Analyst" }, { "id": "analyze_data", "name": "Analyze data", "description": "Perform statistical analysis", "agent": "Strategy Developer" } ] `; const raw = await (0, aiClient_js_1.callAIModel)("Manager Agent", this.model, [ { role: "system", content: "You are an expert task planner." }, { role: "user", content: prompt }, ], verbose); const cleanedRaw = (0, helper_js_1.cleanMarkdownJson)(raw); try { const parsed = JSON.parse(cleanedRaw); if (Array.isArray(parsed)) { return parsed; } throw new Error("Parsed response is not an array"); } catch (error) { if (verbose) console.error("Failed to parse decomposition response:", error); // Fallback: tek gรถrev olarak dรถn return [ { id: "main", name: mainTask.name, description: mainTask.description, agent: mainTask.agent, }, ]; } } async assignAgent(task, agents) { if (task.agent) { const agent = agents.find((a) => a.name === task.agent); if (agent) return agent; } const prompt = `Task: ${task.name} - ${task.description} - task id: ${task.id} Available agents: ${agents.map((a) => `- ${a.name}: ${a.role} / ${a.goal}`).join("\n")} Which agent is the best fit for this task? Return the agent's name as a single string.`; const raw = await (0, aiClient_js_1.callAIModel)("Manager Agent", this.model, [ { role: "system", content: "You are responsible for assigning tasks to agents.", }, { role: "user", content: prompt }, ], this.getVerbose()); const selectedName = raw.trim().replace(/\"/g, ""); (0, log_helper_js_1.TFLog)(`[Manager Agent] Assigning agent for task '${task.name}':\nPrompt:\n${prompt}`, chalk_1.default.green); (0, log_helper_js_1.TFLog)(`[Manager Agent] Assigned agent: ${selectedName}`, chalk_1.default.green); return agents.find((a) => a.name === selectedName) || agents[0]; } async evaluateTaskOutput(task, output, agents) { const availableAgentList = agents.map((a) => `- ${a.name}`).join("\n"); const prompt = `You are an autonomous project manager evaluating the output of a task completed by an agent. Evaluate whether the output sufficiently meets the task description, and decide how to proceed. ONLY respond with a **pure JSON object** matching one of the following formats โ€” do NOT include markdown, text, code block syntax, or explanations. Valid formats: 1. Accept the result: { "action": "accept" } 2. Retry with the same or another agent (optional): { "action": "retry" } or { "action": "retry", "retryWith": "Agent Name", "reason": "Why retry is needed" } 3. Delegate to another agent: { "action": "delegate", "delegateTo": "Agent Name", "reason": "Clear and concise reason for delegation" } --- Available agents: ${availableAgentList} --- Task: Task id: ${task.id} - Task Name: ${task.name} - Task Description: ${task.description} Output: ${output}`; const decisionRaw = await (0, aiClient_js_1.callAIModel)("Manager Agent", this.model, [{ role: "user", content: prompt }], this.getVerbose()); try { const cleaned = (0, helper_js_1.cleanMarkdownJson)(decisionRaw); const parsed = JSON.parse(cleaned); if (!this.taskEvaluationHistory.has(task.id)) { this.taskEvaluationHistory.set(task.id, new Set()); } const history = this.taskEvaluationHistory.get(task.id); const retryName = parsed.retryWith; const delegateName = parsed.delegateTo; if ((retryName && history.has(retryName)) || (delegateName && history.has(delegateName))) { (0, log_helper_js_1.TFLog)(`๐Ÿ›‘ Evaluation loop detected for task '${task.name}'. Agent '${retryName || delegateName}' was already used.`, chalk_1.default.red); return { action: "accept" }; } if (parsed.action === "accept") return { action: "accept" }; if (parsed.action === "retry") { const retryAgent = agents.find((a) => a.name === parsed.retryWith); if (this.getVerbose()) { (0, log_helper_js_1.TFLog)(`๐Ÿ” Retry requested with ${parsed.retryWith || "same agent"}${parsed.reason ? ` โ€” Reason: ${parsed.reason}` : ""}`, chalk_1.default.red); } return { action: "retry", retryWith: retryAgent, reason: parsed.reason, }; } if (parsed.action === "delegate") { const delegateAgent = agents.find((a) => a.name === parsed.delegateTo); if (!delegateAgent) throw new Error("Invalid agent name in delegateTo"); if (this.getVerbose()) { (0, log_helper_js_1.TFLog)(`๐Ÿ“ค Delegating to ${delegateAgent.name} โ€” Reason: ${parsed.reason}`, chalk_1.default.cyan); } return { action: "delegate", delegateTo: delegateAgent, reason: parsed.reason, }; } } catch (err) { (0, log_helper_js_1.TFLog)(`โŒ Failed to parse evaluation decision: ${err.message}`, chalk_1.default.red); } return { action: "retry" }; } async reviewFinalOutput(finalContext) { const prompt = ` Here is the final output of the task force execution: ${JSON.stringify(finalContext, null, 2)} As the manager agent, check if any task output appears invalid, empty, unresolved (like DELEGATE(...)), or asks for more user input (e.g. "please provide"). If everything looks complete, respond with: { "action": "accept" } If outputs look incomplete or delegations failed, respond with: { "action": "replan", "reason": "..." } If critical errors or unsafe instructions are detected, respond with: { "action": "abort", "reason": "..." } `; const result = await (0, aiClient_js_1.callAIModel)("Manager Agent", this.model, [{ role: "user", content: prompt }], this.getVerbose()); return JSON.parse(result); } } exports.SmartManagerAgent = SmartManagerAgent;