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
JavaScript
;
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;