feat: implement parallel agent execution and unified agent registry
- Fix streaming JSON parsing issues (buffer processing on stream end) - Increase max_tokens for tool-enabled requests (16384) - Add parallel tool execution for task_agent, read, glob, grep (up to 3 concurrent) - Register task_agent tool with queue system and concurrency control - Add session-based isolation with parentSessionId tracking - Create unified agent registry mapping agents from: - OpenCode (7 agents: build, plan, explore, general, etc.) - Claude Code (12 agents: code-explorer, code-architect, etc.) - Cursor (3 agents: pair-programmer, cli, chat) - CodeTyper native (6 agents) - Add agent/skill creation system with LLM-based generation - Store custom agents in .codetyper/agents/*.md - Store custom skills in .codetyper/skills/*/SKILL.md
This commit is contained in:
@@ -13,6 +13,7 @@ import { webFetchTool } from "@tools/web-fetch/execute";
|
||||
import { multiEditTool } from "@tools/multi-edit/execute";
|
||||
import { lspTool } from "@tools/lsp";
|
||||
import { applyPatchTool } from "@tools/apply-patch";
|
||||
import { taskAgentTool } from "@tools/task-agent/execute";
|
||||
import {
|
||||
isMCPTool,
|
||||
executeMCPTool,
|
||||
@@ -40,6 +41,7 @@ export const tools: ToolDefinition[] = [
|
||||
webFetchTool,
|
||||
lspTool,
|
||||
applyPatchTool,
|
||||
taskAgentTool,
|
||||
];
|
||||
|
||||
// Tools that are read-only (allowed in chat mode)
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
* Task Agent Tool
|
||||
*
|
||||
* Allows spawning specialized sub-agents for complex tasks.
|
||||
* Implements the agent delegation pattern from claude-code.
|
||||
* Implements the agent delegation pattern from claude-code and opencode.
|
||||
* Supports parallel execution of up to 3 agents simultaneously.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import type { ToolDefinition, ToolContext, ToolResult } from "@/types/tools";
|
||||
import { MULTI_AGENT_DEFAULTS } from "@/constants/multi-agent";
|
||||
|
||||
/**
|
||||
* Agent types available for delegation
|
||||
@@ -85,14 +87,58 @@ const taskAgentSchema = z.object({
|
||||
type TaskAgentParams = z.infer<typeof taskAgentSchema>;
|
||||
|
||||
/**
|
||||
* Active background agents
|
||||
* Active background agents with their results
|
||||
*/
|
||||
const backgroundAgents = new Map<string, {
|
||||
interface BackgroundAgent {
|
||||
id: string;
|
||||
type: AgentType;
|
||||
task: string;
|
||||
startTime: number;
|
||||
promise: Promise<ToolResult>;
|
||||
}>();
|
||||
result?: ToolResult;
|
||||
}
|
||||
|
||||
const backgroundAgents = new Map<string, BackgroundAgent>();
|
||||
|
||||
/**
|
||||
* Currently running foreground agents (for parallel execution limit)
|
||||
*/
|
||||
let runningForegroundAgents = 0;
|
||||
const MAX_CONCURRENT_AGENTS = MULTI_AGENT_DEFAULTS.maxConcurrent; // 3
|
||||
|
||||
/**
|
||||
* Queue for agents waiting to run
|
||||
*/
|
||||
interface QueuedAgent {
|
||||
params: TaskAgentParams;
|
||||
systemPrompt: string;
|
||||
taskPrompt: string;
|
||||
ctx: ToolContext;
|
||||
resolve: (result: ToolResult) => void;
|
||||
reject: (error: Error) => void;
|
||||
}
|
||||
|
||||
const agentQueue: QueuedAgent[] = [];
|
||||
|
||||
/**
|
||||
* Process the agent queue
|
||||
*/
|
||||
const processQueue = async (): Promise<void> => {
|
||||
while (agentQueue.length > 0 && runningForegroundAgents < MAX_CONCURRENT_AGENTS) {
|
||||
const queued = agentQueue.shift();
|
||||
if (!queued) break;
|
||||
|
||||
runningForegroundAgents++;
|
||||
|
||||
executeAgentInternal(queued.params, queued.systemPrompt, queued.taskPrompt, queued.ctx)
|
||||
.then(queued.resolve)
|
||||
.catch(queued.reject)
|
||||
.finally(() => {
|
||||
runningForegroundAgents--;
|
||||
processQueue();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute the task agent tool
|
||||
@@ -228,9 +274,43 @@ const buildTaskPrompt = (params: TaskAgentParams): string => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Run agent in foreground (blocking)
|
||||
* Run agent in foreground with concurrency control
|
||||
*/
|
||||
const runAgentInForeground = async (
|
||||
params: TaskAgentParams,
|
||||
systemPrompt: string,
|
||||
taskPrompt: string,
|
||||
ctx: ToolContext,
|
||||
): Promise<ToolResult> => {
|
||||
// Check if we can run immediately
|
||||
if (runningForegroundAgents < MAX_CONCURRENT_AGENTS) {
|
||||
runningForegroundAgents++;
|
||||
|
||||
try {
|
||||
return await executeAgentInternal(params, systemPrompt, taskPrompt, ctx);
|
||||
} finally {
|
||||
runningForegroundAgents--;
|
||||
processQueue();
|
||||
}
|
||||
}
|
||||
|
||||
// Queue the agent if at capacity
|
||||
return new Promise((resolve, reject) => {
|
||||
agentQueue.push({
|
||||
params,
|
||||
systemPrompt,
|
||||
taskPrompt,
|
||||
ctx,
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute agent internal logic
|
||||
*/
|
||||
const executeAgentInternal = async (
|
||||
params: TaskAgentParams,
|
||||
systemPrompt: string,
|
||||
taskPrompt: string,
|
||||
@@ -296,15 +376,19 @@ const runAgentInBackground = async (
|
||||
|
||||
const promise = runAgentInForeground(params, systemPrompt, taskPrompt, ctx);
|
||||
|
||||
backgroundAgents.set(agentId, {
|
||||
const agent: BackgroundAgent = {
|
||||
id: agentId,
|
||||
type: params.agent_type,
|
||||
task: params.task,
|
||||
startTime: Date.now(),
|
||||
promise,
|
||||
});
|
||||
};
|
||||
|
||||
// Cleanup after completion
|
||||
promise.then(() => {
|
||||
backgroundAgents.set(agentId, agent);
|
||||
|
||||
// Store result and cleanup after completion
|
||||
promise.then((result) => {
|
||||
agent.result = result;
|
||||
// Keep result for 5 minutes
|
||||
setTimeout(() => backgroundAgents.delete(agentId), 5 * 60 * 1000);
|
||||
});
|
||||
@@ -312,7 +396,7 @@ const runAgentInBackground = async (
|
||||
return {
|
||||
success: true,
|
||||
title: `${params.agent_type} agent started in background`,
|
||||
output: `Agent ID: ${agentId}\n\nThe ${params.agent_type} agent is now running in the background.\nUse the agent ID to check status or retrieve results.`,
|
||||
output: `Agent ID: ${agentId}\n\nThe ${params.agent_type} agent is now running in the background.\nUse the agent ID to check status or retrieve results.\n\nCurrent running agents: ${runningForegroundAgents}/${MAX_CONCURRENT_AGENTS}`,
|
||||
metadata: {
|
||||
agentId,
|
||||
agentType: params.agent_type,
|
||||
@@ -338,10 +422,15 @@ export const getBackgroundAgentStatus = async (
|
||||
};
|
||||
}
|
||||
|
||||
// Check if completed
|
||||
// Return cached result if available
|
||||
if (agent.result) {
|
||||
return agent.result;
|
||||
}
|
||||
|
||||
// Check if completed (with short timeout)
|
||||
const result = await Promise.race([
|
||||
agent.promise.then(r => ({ completed: true, result: r })),
|
||||
new Promise<{ completed: false }>(resolve =>
|
||||
agent.promise.then(r => ({ completed: true as const, result: r })),
|
||||
new Promise<{ completed: false }>((resolve) =>
|
||||
setTimeout(() => resolve({ completed: false }), 100),
|
||||
),
|
||||
]);
|
||||
@@ -365,6 +454,21 @@ export const getBackgroundAgentStatus = async (
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get current agent execution status
|
||||
*/
|
||||
export const getAgentExecutionStatus = (): {
|
||||
running: number;
|
||||
queued: number;
|
||||
background: number;
|
||||
maxConcurrent: number;
|
||||
} => ({
|
||||
running: runningForegroundAgents,
|
||||
queued: agentQueue.length,
|
||||
background: backgroundAgents.size,
|
||||
maxConcurrent: MAX_CONCURRENT_AGENTS,
|
||||
});
|
||||
|
||||
/**
|
||||
* Tool definition for task agent
|
||||
*/
|
||||
@@ -380,12 +484,17 @@ Available agent types:
|
||||
- refactor: Code refactoring and improvement
|
||||
- plan: Planning and architecture design
|
||||
|
||||
PARALLEL EXECUTION:
|
||||
- Up to ${MAX_CONCURRENT_AGENTS} agents can run simultaneously
|
||||
- Launch multiple agents in a single message for parallel execution
|
||||
- Agents exceeding the limit will queue automatically
|
||||
|
||||
Use agents when:
|
||||
- Task requires specialized focus
|
||||
- Multiple parallel investigations needed
|
||||
- Complex implementation that benefits from isolation
|
||||
|
||||
Agents run with their own context and tools, returning results when complete.`,
|
||||
Example: To explore 3 areas of the codebase in parallel, call task_agent 3 times in the same message.`,
|
||||
parameters: taskAgentSchema,
|
||||
execute: executeTaskAgent,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user