- Added Claude Code integration with full context compaction support - Added OpenClaw integration with deterministic pipeline support - Implemented parallel agent execution (4 projects x 3 roles pattern) - Added workspace isolation with permissions and quotas - Implemented Lobster-compatible YAML workflow parser - Added persistent memory store for cross-session context - Created comprehensive README with hero section This project was 100% autonomously built by Z.AI GLM-5
456 lines
12 KiB
TypeScript
456 lines
12 KiB
TypeScript
/**
|
|
* Subagent Spawner Module
|
|
*
|
|
* Creates and manages child agents (subagents) for parallel task execution.
|
|
* Implements communication channels, result aggregation, and lifecycle management.
|
|
*/
|
|
|
|
import { randomUUID } from 'crypto';
|
|
import ZAI from 'z-ai-web-dev-sdk';
|
|
import { AgentOrchestrator, AgentConfig, Task, TaskPriority } from './orchestrator';
|
|
|
|
export type SubagentType =
|
|
| 'explorer' // For code exploration
|
|
| 'researcher' // For information gathering
|
|
| 'coder' // For code generation
|
|
| 'reviewer' // For code review
|
|
| 'planner' // For task planning
|
|
| 'executor' // For task execution
|
|
| 'custom'; // Custom subagent
|
|
|
|
export interface SubagentDefinition {
|
|
type: SubagentType;
|
|
name: string;
|
|
description: string;
|
|
systemPrompt: string;
|
|
capabilities: string[];
|
|
maxTasks?: number;
|
|
timeout?: number;
|
|
}
|
|
|
|
export interface SubagentResult {
|
|
subagentId: string;
|
|
taskId: string;
|
|
success: boolean;
|
|
output: unknown;
|
|
error?: string;
|
|
tokens: {
|
|
input: number;
|
|
output: number;
|
|
};
|
|
duration: number;
|
|
}
|
|
|
|
export interface SpawnOptions {
|
|
priority?: TaskPriority;
|
|
timeout?: number;
|
|
context?: string;
|
|
dependencies?: string[];
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface SubagentPool {
|
|
id: string;
|
|
name: string;
|
|
subagents: Map<string, SubagentInstance>;
|
|
createdAt: Date;
|
|
}
|
|
|
|
/**
|
|
* SubagentInstance - A running subagent
|
|
*/
|
|
export class SubagentInstance {
|
|
id: string;
|
|
definition: SubagentDefinition;
|
|
orchestrator: AgentOrchestrator;
|
|
private zai: Awaited<ReturnType<typeof ZAI.create>> | null = null;
|
|
private initialized = false;
|
|
|
|
constructor(
|
|
definition: SubagentDefinition,
|
|
orchestrator: AgentOrchestrator
|
|
) {
|
|
this.id = `${definition.type}-${randomUUID().substring(0, 8)}`;
|
|
this.definition = definition;
|
|
this.orchestrator = orchestrator;
|
|
}
|
|
|
|
/**
|
|
* Initialize the subagent
|
|
*/
|
|
async initialize(): Promise<void> {
|
|
if (this.initialized) return;
|
|
|
|
this.zai = await ZAI.create();
|
|
|
|
// Register with orchestrator
|
|
const config: AgentConfig = {
|
|
id: this.id,
|
|
name: this.definition.name,
|
|
type: this.definition.type,
|
|
capabilities: this.definition.capabilities,
|
|
maxConcurrentTasks: this.definition.maxTasks || 3,
|
|
timeout: this.definition.timeout || 60000,
|
|
metadata: {
|
|
systemPrompt: this.definition.systemPrompt
|
|
}
|
|
};
|
|
|
|
this.orchestrator.registerAgent(config);
|
|
this.initialized = true;
|
|
}
|
|
|
|
/**
|
|
* Execute a task
|
|
*/
|
|
async execute(input: string, context?: string): Promise<SubagentResult> {
|
|
const startTime = Date.now();
|
|
|
|
if (!this.initialized || !this.zai) {
|
|
await this.initialize();
|
|
}
|
|
|
|
const task = this.orchestrator.createTask(
|
|
this.definition.type,
|
|
`Execute ${this.definition.type} task`,
|
|
{ input, context },
|
|
{ assignedAgent: this.id }
|
|
);
|
|
|
|
try {
|
|
const messages = [
|
|
{
|
|
role: 'assistant' as const,
|
|
content: this.definition.systemPrompt
|
|
},
|
|
{
|
|
role: 'user' as const,
|
|
content: context
|
|
? `Context: ${context}\n\nTask: ${input}`
|
|
: input
|
|
}
|
|
];
|
|
|
|
const response = await this.zai!.chat.completions.create({
|
|
messages,
|
|
thinking: { type: 'disabled' }
|
|
});
|
|
|
|
const output = response.choices?.[0]?.message?.content || '';
|
|
|
|
const result: SubagentResult = {
|
|
subagentId: this.id,
|
|
taskId: task.id,
|
|
success: true,
|
|
output,
|
|
tokens: {
|
|
input: 0, // Would need tokenizer to calculate
|
|
output: 0
|
|
},
|
|
duration: Date.now() - startTime
|
|
};
|
|
|
|
return result;
|
|
|
|
} catch (error) {
|
|
return {
|
|
subagentId: this.id,
|
|
taskId: task.id,
|
|
success: false,
|
|
output: null,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
tokens: { input: 0, output: 0 },
|
|
duration: Date.now() - startTime
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Terminate the subagent
|
|
*/
|
|
terminate(): void {
|
|
this.orchestrator.unregisterAgent(this.id);
|
|
this.initialized = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SubagentSpawner - Factory for creating and managing subagents
|
|
*/
|
|
export class SubagentSpawner {
|
|
private orchestrator: AgentOrchestrator;
|
|
private subagents: Map<string, SubagentInstance> = new Map();
|
|
private pools: Map<string, SubagentPool> = new Map();
|
|
private definitions: Map<SubagentType, SubagentDefinition> = new Map();
|
|
|
|
constructor(orchestrator?: AgentOrchestrator) {
|
|
this.orchestrator = orchestrator || new AgentOrchestrator();
|
|
this.registerDefaultDefinitions();
|
|
this.orchestrator.start();
|
|
}
|
|
|
|
/**
|
|
* Register default subagent definitions
|
|
*/
|
|
private registerDefaultDefinitions(): void {
|
|
const defaults: SubagentDefinition[] = [
|
|
{
|
|
type: 'explorer',
|
|
name: 'Code Explorer',
|
|
description: 'Explores codebases to find relevant files and code',
|
|
systemPrompt: `You are a code explorer agent. Your job is to search through codebases to find relevant files, functions, and code patterns. Be thorough but concise in your findings.`,
|
|
capabilities: ['explore', 'search', 'find']
|
|
},
|
|
{
|
|
type: 'researcher',
|
|
name: 'Research Agent',
|
|
description: 'Gathers information and researches topics',
|
|
systemPrompt: `You are a research agent. Your job is to gather comprehensive information on given topics. Focus on accuracy and completeness.`,
|
|
capabilities: ['research', 'gather', 'analyze']
|
|
},
|
|
{
|
|
type: 'coder',
|
|
name: 'Code Generator',
|
|
description: 'Generates code based on specifications',
|
|
systemPrompt: `You are a code generation agent. Your job is to write clean, efficient, and well-documented code. Follow best practices and include appropriate error handling.`,
|
|
capabilities: ['code', 'generate', 'implement']
|
|
},
|
|
{
|
|
type: 'reviewer',
|
|
name: 'Code Reviewer',
|
|
description: 'Reviews code for quality, bugs, and improvements',
|
|
systemPrompt: `You are a code review agent. Your job is to analyze code for bugs, security issues, performance problems, and best practice violations. Provide constructive feedback.`,
|
|
capabilities: ['review', 'analyze', 'validate']
|
|
},
|
|
{
|
|
type: 'planner',
|
|
name: 'Task Planner',
|
|
description: 'Plans and breaks down complex tasks',
|
|
systemPrompt: `You are a planning agent. Your job is to break down complex tasks into smaller, manageable steps. Consider dependencies and optimal execution order.`,
|
|
capabilities: ['plan', 'decompose', 'organize']
|
|
},
|
|
{
|
|
type: 'executor',
|
|
name: 'Task Executor',
|
|
description: 'Executes specific tasks with precision',
|
|
systemPrompt: `You are an execution agent. Your job is to carry out specific tasks accurately and efficiently. Report results clearly and flag any issues encountered.`,
|
|
capabilities: ['execute', 'run', 'process']
|
|
}
|
|
];
|
|
|
|
for (const def of defaults) {
|
|
this.definitions.set(def.type, def);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register a custom subagent definition
|
|
*/
|
|
registerDefinition(definition: SubagentDefinition): void {
|
|
this.definitions.set(definition.type, definition);
|
|
}
|
|
|
|
/**
|
|
* Spawn a single subagent
|
|
*/
|
|
async spawn(type: SubagentType): Promise<SubagentInstance> {
|
|
const definition = this.definitions.get(type);
|
|
if (!definition) {
|
|
throw new Error(`Unknown subagent type: ${type}`);
|
|
}
|
|
|
|
const subagent = new SubagentInstance(definition, this.orchestrator);
|
|
await subagent.initialize();
|
|
this.subagents.set(subagent.id, subagent);
|
|
|
|
return subagent;
|
|
}
|
|
|
|
/**
|
|
* Spawn multiple subagents of the same type
|
|
*/
|
|
async spawnPool(
|
|
type: SubagentType,
|
|
count: number,
|
|
poolName?: string
|
|
): Promise<SubagentPool> {
|
|
const pool: SubagentPool = {
|
|
id: randomUUID(),
|
|
name: poolName || `${type}-pool-${Date.now()}`,
|
|
subagents: new Map(),
|
|
createdAt: new Date()
|
|
};
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const subagent = await this.spawn(type);
|
|
pool.subagents.set(subagent.id, subagent);
|
|
}
|
|
|
|
this.pools.set(pool.id, pool);
|
|
return pool;
|
|
}
|
|
|
|
/**
|
|
* Execute task with a spawned subagent
|
|
*/
|
|
async executeWithSubagent(
|
|
type: SubagentType,
|
|
task: string,
|
|
context?: string,
|
|
options?: SpawnOptions
|
|
): Promise<SubagentResult> {
|
|
const subagent = await this.spawn(type);
|
|
|
|
try {
|
|
const result = await subagent.execute(task, context);
|
|
return result;
|
|
} finally {
|
|
// Auto-terminate after execution
|
|
subagent.terminate();
|
|
this.subagents.delete(subagent.id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute multiple tasks in parallel
|
|
*/
|
|
async executeParallel(
|
|
tasks: Array<{
|
|
type: SubagentType;
|
|
input: string;
|
|
context?: string;
|
|
}>,
|
|
options?: { maxConcurrent?: number }
|
|
): Promise<SubagentResult[]> {
|
|
const maxConcurrent = options?.maxConcurrent || 5;
|
|
const results: SubagentResult[] = [];
|
|
|
|
// Process in batches
|
|
for (let i = 0; i < tasks.length; i += maxConcurrent) {
|
|
const batch = tasks.slice(i, i + maxConcurrent);
|
|
const batchPromises = batch.map(t =>
|
|
this.executeWithSubagent(t.type, t.input, t.context)
|
|
);
|
|
|
|
const batchResults = await Promise.all(batchPromises);
|
|
results.push(...batchResults);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Execute tasks in a pipeline (sequential with context passing)
|
|
*/
|
|
async executePipeline(
|
|
steps: Array<{
|
|
type: SubagentType;
|
|
input: string | ((prevResult: unknown) => string);
|
|
}>,
|
|
initialContext?: string
|
|
): Promise<{ results: SubagentResult[]; finalOutput: unknown }> {
|
|
const results: SubagentResult[] = [];
|
|
let currentContext = initialContext;
|
|
let currentOutput: unknown = null;
|
|
|
|
for (const step of steps) {
|
|
const input = typeof step.input === 'function'
|
|
? step.input(currentOutput)
|
|
: step.input;
|
|
|
|
const result = await this.executeWithSubagent(
|
|
step.type,
|
|
input,
|
|
currentContext
|
|
);
|
|
|
|
results.push(result);
|
|
|
|
if (result.success) {
|
|
currentOutput = result.output;
|
|
currentContext = typeof result.output === 'string'
|
|
? result.output
|
|
: JSON.stringify(result.output);
|
|
} else {
|
|
// Stop pipeline on failure
|
|
break;
|
|
}
|
|
}
|
|
|
|
return { results, finalOutput: currentOutput };
|
|
}
|
|
|
|
/**
|
|
* Terminate a specific subagent
|
|
*/
|
|
terminate(subagentId: string): boolean {
|
|
const subagent = this.subagents.get(subagentId);
|
|
if (subagent) {
|
|
subagent.terminate();
|
|
this.subagents.delete(subagentId);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Terminate all subagents in a pool
|
|
*/
|
|
terminatePool(poolId: string): boolean {
|
|
const pool = this.pools.get(poolId);
|
|
if (!pool) return false;
|
|
|
|
for (const subagent of pool.subagents.values()) {
|
|
subagent.terminate();
|
|
this.subagents.delete(subagent.id);
|
|
}
|
|
|
|
this.pools.delete(poolId);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Terminate all subagents
|
|
*/
|
|
terminateAll(): void {
|
|
for (const subagent of this.subagents.values()) {
|
|
subagent.terminate();
|
|
}
|
|
this.subagents.clear();
|
|
this.pools.clear();
|
|
}
|
|
|
|
/**
|
|
* Get active subagents
|
|
*/
|
|
getActiveSubagents(): SubagentInstance[] {
|
|
return Array.from(this.subagents.values());
|
|
}
|
|
|
|
/**
|
|
* Get orchestrator stats
|
|
*/
|
|
getStats() {
|
|
return {
|
|
activeSubagents: this.subagents.size,
|
|
pools: this.pools.size,
|
|
orchestrator: this.orchestrator.getStats()
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Quick spawn function for simple use cases
|
|
*/
|
|
export async function spawnAndExecute(
|
|
type: SubagentType,
|
|
task: string,
|
|
context?: string
|
|
): Promise<SubagentResult> {
|
|
const spawner = new SubagentSpawner();
|
|
return spawner.executeWithSubagent(type, task, context);
|
|
}
|
|
|
|
// Default spawner instance
|
|
export const defaultSpawner = new SubagentSpawner();
|