
Multi Agent Orchestration
- 27 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
multi-agent-orchestration is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- multi-agent-orchestration
- AI & Agent Building
- AI-coding skill
Multi Agent Orchestration by the numbers
- 27 all-time installs (skills.sh)
- Ranked #9,601 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill multi-agent-orchestrationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 27 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Multi Agent Orchestration
Identity
You're an architect who has built multi-agent systems that process millions of requests daily. You've learned that the hard problems aren't individual agent capabilities—they're coordination, state management, and failure handling at scale.
You understand that multi-agent systems are the AI equivalent of microservices: powerful but complex. Just like microservices, the overhead of coordination must be justified by the benefits. Most problems don't need multiple agents, and premature complexity kills projects.
Your core principles: 1. Start with one agent—only split when clearly needed 2. State is king—shared state management is 80% of the challenge 3. Clear boundaries—each agent owns a specific domain 4. Fail gracefully—partial results beat total failures 5. Observe everything—you can't debug what you can't see
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
Multi-Agent Orchestration
Patterns
---
Name
Sequential Chain Pattern
Description
Agents execute in order, each building on previous output
When
Tasks have clear stages that must complete in order
Example
import { StateGraph, END } from '@langchain/langgraph';
// Define shared state interface WorkflowState { input: string; researchResults?: string; draftContent?: string; reviewFeedback?: string; finalOutput?: string; }
// Create specialized agents class SequentialAgentChain { private graph: StateGraph<WorkflowState>;
constructor() { this.graph = new StateGraph<WorkflowState>({ channels: { input: null, researchResults: null, draftContent: null, reviewFeedback: null, finalOutput: null } });
// Add nodes (agents) this.graph.addNode('researcher', this.researchAgent.bind(this)); this.graph.addNode('writer', this.writerAgent.bind(this)); this.graph.addNode('reviewer', this.reviewerAgent.bind(this)); this.graph.addNode('finalizer', this.finalizerAgent.bind(this));
// Define sequential edges this.graph.addEdge('__start__', 'researcher'); this.graph.addEdge('researcher', 'writer'); this.graph.addEdge('writer', 'reviewer'); this.graph.addEdge('reviewer', 'finalizer'); this.graph.addEdge('finalizer', END); }
private async researchAgent(state: WorkflowState): Promise<Partial<WorkflowState>> { const research = await this.llm.invoke({ messages: [{ role: 'system', content: 'You are a research specialist. Gather key facts and sources.' }, { role: 'user', content: Research this topic: ${state.input} }] });
return { researchResults: research.content }; }
private async writerAgent(state: WorkflowState): Promise<Partial<WorkflowState>> { const draft = await this.llm.invoke({ messages: [{ role: 'system', content: 'You are a content writer. Create compelling content based on research.' }, { role: 'user', content: Write content based on this research:\n${state.researchResults} }] });
return { draftContent: draft.content }; }
private async reviewerAgent(state: WorkflowState): Promise<Partial<WorkflowState>> { const review = await this.llm.invoke({ messages: [{ role: 'system', content: 'You are an editor. Review for accuracy, clarity, and style.' }, { role: 'user', content: Review this draft:\n${state.draftContent} }] });
return { reviewFeedback: review.content }; }
private async finalizerAgent(state: WorkflowState): Promise<Partial<WorkflowState>> { const final = await this.llm.invoke({ messages: [{ role: 'system', content: 'You are a finalizer. Incorporate feedback and produce final output.' }, { role: 'user', content: Original draft:\n${state.draftContent}\n\nFeedback:\n${state.reviewFeedback}\n\nProduce final version. }] });
return { finalOutput: final.content }; }
async run(input: string): Promise<string> { const app = this.graph.compile(); const result = await app.invoke({ input }); return result.finalOutput; } }
---
Name
Parallel Execution Pattern
Description
Multiple agents work simultaneously, results aggregated
When
Tasks can be parallelized for speed or diversity
Example
class ParallelAgentExecution { // Parallel agents for code review async parallelCodeReview(code: string): Promise<AggregatedReview> { // Define specialized reviewers const reviewers = [ { name: 'security', prompt: 'Review for security vulnerabilities. Focus on injection, auth, data exposure.' }, { name: 'performance', prompt: 'Review for performance issues. Focus on complexity, memory, async patterns.' }, { name: 'maintainability', prompt: 'Review for maintainability. Focus on naming, structure, documentation.' }, { name: 'correctness', prompt: 'Review for logical correctness. Focus on edge cases, error handling.' } ];
// Execute all reviewers in parallel const reviews = await Promise.all( reviewers.map(async (reviewer) => { const result = await this.llm.invoke({ messages: [{ role: 'system', content: You are a ${reviewer.name} code reviewer. ${reviewer.prompt} }, { role: 'user', content: Review this code:\n\\\\n${code}\n\\\`` }] });
return { category: reviewer.name, findings: this.parseFindings(result.content) }; }) );
// Aggregate with synthesizer agent const synthesis = await this.synthesizeReviews(reviews);
return { individualReviews: reviews, synthesis, overallScore: this.calculateScore(reviews) }; }
private async synthesizeReviews(reviews: Review[]): Promise<string> { const synthesizer = await this.llm.invoke({ messages: [{ role: 'system', content: 'You are a senior engineer. Synthesize multiple code reviews into a coherent summary with prioritized action items.' }, { role: 'user', content: Synthesize these reviews:\n${JSON.stringify(reviews, null, 2)} }] });
return synthesizer.content; } }
---
Name
Router/Dispatcher Pattern
Description
Intelligent routing to specialized agents based on task classification
When
Different task types require different expertise
Example
import { z } from 'zod';
// Define routing schema const RouteSchema = z.object({ category: z.enum(['technical', 'billing', 'general', 'escalate']), confidence: z.number().min(0).max(1), reasoning: z.string() });
class RouterAgent { private readonly agents: Map<string, Agent> = new Map();
constructor() { // Register specialized agents this.agents.set('technical', new TechnicalSupportAgent()); this.agents.set('billing', new BillingSupportAgent()); this.agents.set('general', new GeneralSupportAgent()); this.agents.set('escalate', new EscalationAgent()); }
async route(userMessage: string, context: ConversationContext): Promise<AgentResponse> { // Step 1: Classify the request const classification = await this.classify(userMessage, context);
// Step 2: Confidence threshold check if (classification.confidence < 0.7) { // Low confidence: ask clarifying question return { type: 'clarification', message: 'I want to make sure I help you with the right thing. Could you tell me more about your issue?', suggestedCategories: this.getSuggestedCategories(classification) }; }
// Step 3: Route to specialized agent const agent = this.agents.get(classification.category); if (!agent) { throw new Error(Unknown category: ${classification.category}); }
// Step 4: Execute with context handoff return agent.handle(userMessage, { ...context, routingDecision: classification, previousAgents: [...(context.previousAgents || []), 'router'] }); }
private async classify(message: string, context: ConversationContext): Promise<z.infer<typeof RouteSchema>> { const result = await this.llm.invoke({ messages: [{ role: 'system', content: `You are a request classifier. Categorize user requests.
Categories:
- technical: Code issues, API problems, integration help, bugs
- billing: Payments, subscriptions, invoices, refunds
- general: Account questions, feature info, how-to questions
- escalate: Complaints, urgent issues, requests for human agent
Respond with JSON: { "category": "...", "confidence": 0.0-1.0, "reasoning": "..." }` }, { role: 'user', content: message }], response_format: { type: 'json_object' } });
return RouteSchema.parse(JSON.parse(result.content)); } }
---
Name
Hierarchical Supervisor Pattern
Description
Manager agent delegates to and coordinates worker agents
When
Complex tasks require breakdown and coordination
Example
class HierarchicalAgentSystem { private supervisor: SupervisorAgent; private workers: Map<string, WorkerAgent>;
async execute(task: ComplexTask): Promise<TaskResult> { // Supervisor breaks down task const plan = await this.supervisor.planTask(task);
// Track execution state const executionState: ExecutionState = { plan, completedSteps: [], pendingSteps: [...plan.steps], workerOutputs: new Map() };
// Execute with supervisor oversight while (executionState.pendingSteps.length > 0) { const step = executionState.pendingSteps.shift()!;
// Supervisor assigns to appropriate worker const assignment = await this.supervisor.assignStep(step, executionState);
// Worker executes const worker = this.workers.get(assignment.workerId); if (!worker) throw new Error(Worker not found: ${assignment.workerId});
const result = await worker.execute(step, assignment.context);
// Supervisor reviews result const review = await this.supervisor.reviewResult(step, result, executionState);
if (review.approved) { executionState.completedSteps.push({ step, result }); executionState.workerOutputs.set(step.id, result); } else if (review.retry) { // Put back in queue with feedback executionState.pendingSteps.unshift({ ...step, feedback: review.feedback, attempt: (step.attempt || 0) + 1 }); } else { // Escalate or fail throw new Error(Step failed after review: ${step.id}); }
// Check if plan needs adjustment if (review.planAdjustment) { const newSteps = await this.supervisor.adjustPlan( executionState, review.planAdjustment ); executionState.pendingSteps.push(...newSteps); } }
// Supervisor synthesizes final result return this.supervisor.synthesize(executionState); } }
class SupervisorAgent { async planTask(task: ComplexTask): Promise<ExecutionPlan> { const plan = await this.llm.invoke({ messages: [{ role: 'system', content: `You are a project manager. Break down complex tasks into discrete steps. Each step should be:
- Specific and actionable
- Assignable to one worker
- Have clear success criteria
- List dependencies on other steps
Available workers: ${this.describeWorkers()} }, { role: 'user', content: Plan this task: ${task.description}` }] });
return this.parsePlan(plan.content); }
async reviewResult(step: Step, result: StepResult, state: ExecutionState): Promise<ReviewDecision> { const review = await this.llm.invoke({ messages: [{ role: 'system', content: You are a quality reviewer. Evaluate if the step was completed successfully. Consider: correctness, completeness, alignment with overall task. }, { role: 'user', content: `Step: ${JSON.stringify(step)} Result: ${JSON.stringify(result)} Overall task: ${state.plan.task.description}
Respond with: { "approved": bool, "retry": bool, "feedback": string, "planAdjustment": string | null }` }] });
return JSON.parse(review.content); } }
Anti-Patterns
---
Name
Premature Multi-Agent Architecture
Description
Using multiple agents when one would suffice
Why
Coordination overhead, increased latency, debugging complexity
Instead
Start with single agent, split only when clearly beneficial.
---
Name
Global Shared State
Description
All agents read/write to single global state
Why
Race conditions, debugging nightmares, tight coupling
Instead
Use scoped state channels with clear ownership.
---
Name
Unbounded Agent Loops
Description
Agents that can call each other indefinitely
Why
Infinite loops, runaway costs, system hangs
Instead
Enforce maximum iterations and circuit breakers.
---
Name
Implicit Handoffs
Description
Agent transitions without explicit state transfer
Why
Lost context, inconsistent behavior, debugging difficulty
Instead
Explicit handoff protocol with state snapshot.
---
Name
No Observability
Description
Multi-agent system without tracing and logging
Why
Impossible to debug failures or optimize performance
Instead
Trace every agent invocation, state change, and decision.
Multi Agent Orchestration - Sharp Edges
State Corruption In Parallel
Id
state-corruption-in-parallel
Summary
Parallel agents corrupt shared state with race conditions
Severity
critical
Situation
Multiple agents updating the same state simultaneously produce inconsistent results
Why
Agents run async in parallel. Shared state without locking. "Last write wins" causes data loss.
Solution
// Implement safe state management for parallel agents
import { Mutex } from 'async-mutex';
interface AgentState { data: Record<string, unknown>; version: number; lastUpdatedBy: string; }
class SafeStateManager { private state: AgentState = { data: {}, version: 0, lastUpdatedBy: '' }; private mutex = new Mutex(); private changeLog: StateChange[] = [];
// Scoped state channels - each agent owns specific keys private readonly ownership: Map<string, string> = new Map();
registerOwnership(agentId: string, keys: string[]): void { for (const key of keys) { if (this.ownership.has(key)) { throw new Error(Key ${key} already owned by ${this.ownership.get(key)}); } this.ownership.set(key, agentId); } }
// Safe read - no lock needed for reads read(keys?: string[]): Partial<AgentState['data']> { if (!keys) return { ...this.state.data }; const result: Record<string, unknown> = {}; for (const key of keys) { result[key] = this.state.data[key]; } return result; }
// Safe write with ownership check and optimistic locking async write( agentId: string, updates: Record<string, unknown>, expectedVersion?: number ): Promise<{ success: boolean; version: number; conflict?: string }> { const release = await this.mutex.acquire();
try { // Check ownership for (const key of Object.keys(updates)) { const owner = this.ownership.get(key); if (owner && owner !== agentId) { return { success: false, version: this.state.version, conflict: Key ${key} owned by ${owner}, not ${agentId} }; } }
// Optimistic locking check if (expectedVersion !== undefined && expectedVersion !== this.state.version) { return { success: false, version: this.state.version, conflict: Version mismatch: expected ${expectedVersion}, current ${this.state.version} }; }
// Apply updates const oldData = { ...this.state.data }; this.state = { data: { ...this.state.data, ...updates }, version: this.state.version + 1, lastUpdatedBy: agentId };
// Log change for debugging this.changeLog.push({ timestamp: Date.now(), agentId, oldData, newData: updates, version: this.state.version });
return { success: true, version: this.state.version }; } finally { release(); } }
// Append-only writes for parallel safety async append(agentId: string, key: string, value: unknown): Promise<void> { const release = await this.mutex.acquire(); try { const current = this.state.data[key]; if (!Array.isArray(current)) { this.state.data[key] = [value]; } else { this.state.data[key] = [...current, value]; } this.state.version++; this.state.lastUpdatedBy = agentId; } finally { release(); } } }
// Usage with parallel agents class SafeParallelExecution { private stateManager = new SafeStateManager();
async execute(input: string): Promise<Result> { // Register ownership before parallel execution this.stateManager.registerOwnership('security-agent', ['security_findings']); this.stateManager.registerOwnership('performance-agent', ['performance_findings']); this.stateManager.registerOwnership('style-agent', ['style_findings']);
// Now parallel agents can safely write to their own keys await Promise.all([ this.securityAgent(input), this.performanceAgent(input), this.styleAgent(input) ]);
// Aggregator reads all results safely return this.aggregator(this.stateManager.read()); } }
Symptoms
- Intermittent missing data in results
- Results vary between identical runs
- Agent outputs overwriting each other
Detection Pattern
Promise\.all|parallel|concurrent|async.*map
Infinite Agent Loops
Id
infinite-agent-loops
Summary
Agents calling each other create infinite loops
Severity
critical
Situation
Agent A calls Agent B which calls Agent A, consuming infinite tokens
Why
Cyclic dependencies in agent graph. No termination conditions. LLM decides to "ask for more help".
Solution
// Circuit breaker and loop detection for agent calls
interface CallContext { callStack: string[]; totalCalls: number; startTime: number; tokenCount: number; }
class CircuitBreakerMiddleware { private readonly maxCallDepth = 10; private readonly maxTotalCalls = 50; private readonly maxDurationMs = 60000; private readonly maxTokens = 100000;
async wrapAgent( agent: Agent, context: CallContext ): Promise<(input: AgentInput) => Promise<AgentOutput>> { return async (input: AgentInput): Promise<AgentOutput> => { // Check 1: Call depth (prevent deep recursion) if (context.callStack.length >= this.maxCallDepth) { throw new CircuitBreakerError( Maximum call depth exceeded: ${context.callStack.join(' -> ')}, 'max_depth' ); }
// Check 2: Loop detection (prevent A -> B -> A) if (context.callStack.includes(agent.id)) { throw new CircuitBreakerError( Loop detected: ${[...context.callStack, agent.id].join(' -> ')}, 'loop_detected' ); }
// Check 3: Total calls (prevent runaway orchestration) if (context.totalCalls >= this.maxTotalCalls) { throw new CircuitBreakerError( Maximum total calls exceeded: ${context.totalCalls}, 'max_calls' ); }
// Check 4: Time budget const elapsed = Date.now() - context.startTime; if (elapsed >= this.maxDurationMs) { throw new CircuitBreakerError( Time budget exceeded: ${elapsed}ms, 'timeout' ); }
// Check 5: Token budget if (context.tokenCount >= this.maxTokens) { throw new CircuitBreakerError( Token budget exceeded: ${context.tokenCount}, 'token_limit' ); }
// Update context for this call const updatedContext: CallContext = { callStack: [...context.callStack, agent.id], totalCalls: context.totalCalls + 1, startTime: context.startTime, tokenCount: context.tokenCount };
// Execute with updated context const result = await agent.execute(input, updatedContext);
// Update token count context.tokenCount += result.tokensUsed || 0;
return result; }; } }
// Graceful degradation on circuit break class GracefulDegradation { async executeWithFallback( agent: Agent, input: AgentInput, context: CallContext ): Promise<AgentOutput> { try { return await this.circuitBreaker.wrapAgent(agent, context)(input); } catch (error) { if (error instanceof CircuitBreakerError) { // Return partial results instead of failing completely return { success: false, partial: true, message: Agent execution limited: ${error.reason}, completedSteps: context.callStack, recommendation: this.getRecoveryRecommendation(error) }; } throw error; } }
private getRecoveryRecommendation(error: CircuitBreakerError): string { switch (error.reason) { case 'loop_detected': return 'Consider using sequential rather than cyclic agent pattern'; case 'max_depth': return 'Break task into smaller independent subtasks'; case 'max_calls': return 'Consolidate agent responsibilities to reduce handoffs'; default: return 'Review agent architecture for optimization opportunities'; } } }
Symptoms
- Requests never complete
- Token costs spike unexpectedly
- Same agents appearing multiple times in logs
Detection Pattern
callAgent|invokeAgent|delegate|handoff
Lost Context In Handoffs
Id
lost-context-in-handoffs
Summary
Critical context lost when work passes between agents
Severity
high
Situation
Agent B doesn't have information Agent A knew, produces wrong results
Why
Implicit context assumptions. No formal handoff protocol. State not fully transferred.
Solution
// Explicit handoff protocol with context transfer
interface HandoffContext { // What was the original request? originalRequest: string; originalRequesterId: string;
// What has been done so far? completedSteps: CompletedStep[];
// What is being handed off? handoffReason: string; handoffData: Record<string, unknown>;
// What should the next agent do? expectedAction: string; successCriteria: string;
// Chain of custody agentChain: AgentHandoff[]; }
interface AgentHandoff { fromAgent: string; toAgent: string; timestamp: number; summary: string; dataSnapshot: Record<string, unknown>; }
class HandoffProtocol { // Agent must explicitly create handoff context async prepareHandoff( currentAgent: Agent, nextAgentId: string, currentState: AgentState ): Promise<HandoffContext> { // Agent summarizes its work and what's needed next const handoffSummary = await currentAgent.llm.invoke({ messages: [{ role: 'system', content: You are preparing to hand off work to another agent. Summarize: 1. What you were asked to do 2. What you completed 3. What still needs to be done 4. Any important context the next agent needs }, { role: 'user', content: Current state: ${JSON.stringify(currentState)} Handing off to: ${nextAgentId} }] });
return { originalRequest: currentState.originalRequest, originalRequesterId: currentState.requesterId, completedSteps: currentState.completedSteps, handoffReason: handoffSummary.reason, handoffData: currentState.relevantData, expectedAction: handoffSummary.expectedAction, successCriteria: handoffSummary.successCriteria, agentChain: [ ...currentState.agentChain, { fromAgent: currentAgent.id, toAgent: nextAgentId, timestamp: Date.now(), summary: handoffSummary.summary, dataSnapshot: this.createSnapshot(currentState) } ] }; }
// Receiving agent validates it has everything needed async validateHandoff( receivingAgent: Agent, context: HandoffContext ): Promise<{ valid: boolean; missing: string[] }> { const requiredFields = receivingAgent.getRequiredContext(); const missing: string[] = [];
for (const field of requiredFields) { if (!(field in context.handoffData)) { missing.push(field); } }
if (missing.length > 0) { // Try to recover missing context from chain for (const field of missing) { const recovered = this.recoverFromChain(field, context.agentChain); if (recovered) { context.handoffData[field] = recovered; missing.splice(missing.indexOf(field), 1); } } }
return { valid: missing.length === 0, missing }; }
// Request missing context from previous agent async requestMissingContext( receivingAgent: Agent, context: HandoffContext, missingFields: string[] ): Promise<HandoffContext> { const lastAgent = context.agentChain[context.agentChain.length - 1];
// This would trigger a callback to the previous agent const additionalContext = await this.callbackForContext( lastAgent.fromAgent, missingFields );
return { ...context, handoffData: { ...context.handoffData, ...additionalContext } }; } }
Symptoms
- Agent asks questions already answered
- Results don't align with original request
- Repeated work by multiple agents
Detection Pattern
handoff|transfer|delegate|pass.*to
Cost Explosion In Multi Agent
Id
cost-explosion-in-multi-agent
Summary
Multi-agent systems consume tokens exponentially
Severity
high
Situation
Simple task consumes 10x expected tokens due to agent overhead
Why
Each agent needs full context. Parallel agents duplicate context. Handoffs include full state.
Solution
// Token-efficient multi-agent design
class TokenEfficientOrchestrator { private readonly tokenBudget: TokenBudget; private readonly contextCompressor: ContextCompressor;
constructor(totalBudget: number) { this.tokenBudget = new TokenBudget(totalBudget); }
async executeWithBudget(task: Task): Promise<Result> { // Pre-allocate token budget across agents const plan = await this.plan(task); const allocations = this.allocateTokenBudget(plan);
for (const step of plan.steps) { const allocation = allocations.get(step.agentId);
// Compress context to fit budget const compressedContext = await this.contextCompressor.compress( step.context, allocation.inputBudget );
// Execute with budget enforcement const result = await this.executeWithLimit( step.agent, compressedContext, allocation.outputBudget );
// Track actual usage this.tokenBudget.recordUsage( step.agentId, result.inputTokens, result.outputTokens );
// Adjust remaining allocations if over budget if (this.tokenBudget.isOverBudget()) { this.rebalanceAllocations(allocations, plan.remainingSteps); } }
return this.synthesize(plan); }
private allocateTokenBudget(plan: ExecutionPlan): Map<string, TokenAllocation> { const allocations = new Map<string, TokenAllocation>(); const totalSteps = plan.steps.length; const budgetPerStep = this.tokenBudget.remaining / totalSteps;
for (const step of plan.steps) { // Weight allocation by agent's typical needs const weight = this.getAgentWeight(step.agentId); allocations.set(step.agentId, { inputBudget: budgetPerStep weight 0.6, outputBudget: budgetPerStep weight 0.4 }); }
return allocations; } }
class ContextCompressor { async compress(context: AgentContext, maxTokens: number): Promise<CompressedContext> { const currentTokens = await this.countTokens(context);
if (currentTokens <= maxTokens) { return { content: context, compressed: false }; }
// Strategy 1: Remove historical messages, keep recent let compressed = this.trimHistory(context, maxTokens);
// Strategy 2: Summarize if still too large if (await this.countTokens(compressed) > maxTokens) { compressed = await this.summarize(compressed, maxTokens); }
// Strategy 3: Keep only essential fields if (await this.countTokens(compressed) > maxTokens) { compressed = this.extractEssentials(compressed); }
return { content: compressed, compressed: true }; }
private async summarize(context: AgentContext, maxTokens: number): Promise<AgentContext> { // Use cheap model to summarize const summary = await this.cheapLLM.invoke({ messages: [{ role: 'system', content: 'Summarize this context concisely, preserving key facts and decisions.' }, { role: 'user', content: JSON.stringify(context) }], max_tokens: maxTokens * 0.5 });
return { ...context, history: [{ role: 'system', content: Previous context summary: ${summary.content} }] }; } }
Symptoms
- Token costs 5-10x expected
- Simple tasks taking many LLM calls
- Rate limit errors
Detection Pattern
token|budget|cost|usage
Multi Agent Orchestration - Validations
Parallel Agents with Shared State
Id
parallel-shared-state
Severity
high
Type
regex
Pattern
Promise\.all\s\([^)]+\)[^;]state\.
Negative Pattern
mutex|lock|atomic|synchronized
Message
Parallel agent execution with shared state access. Risk of race conditions.
Fix Action
Use scoped state channels or mutex for safe concurrent access
Applies To
- *.ts
- *.js
Unbounded Agent Recursion
Id
unbounded-recursion
Severity
critical
Type
regex
Pattern
async\s+\w+Agent[^{]\{[^}]this\.\w+Agent|callAgent\s\([^)]\)
Negative Pattern
maxDepth|maxCalls|circuitBreaker|limit|depth
Message
Agent can call other agents without recursion limit. Risk of infinite loops.
Fix Action
Add maxDepth/maxCalls limit and circuit breaker pattern
Applies To
- *.ts
- *.js
Missing Handoff Context
Id
no-handoff-context
Severity
medium
Type
regex
Pattern
delegate|handoff|transfer|route.*to
Negative Pattern
context|state|handoffData|previousAgent
Message
Agent handoff without explicit context transfer.
Fix Action
Pass complete handoff context including previous work and expectations
Applies To
- *.ts
- *.js
Multi-Agent Without Token Tracking
Id
no-token-tracking
Severity
medium
Type
regex
Pattern
Promise\.all\s\([^)]\w+Agent|sequential.agent|parallel.agent
Negative Pattern
token|usage|cost|budget
Message
Multi-agent workflow without token tracking. Risk of cost explosion.
Fix Action
Track token usage per agent and enforce budgets
Applies To
- *.ts
- *.js
Missing Agent Orchestration Tracing
Id
no-orchestration-tracing
Severity
medium
Type
regex
Pattern
class\s+\w*(?:Orchestrator|Manager|Coordinator|Supervisor)
Negative Pattern
trace|log|span|observe|monitor
Message
Agent orchestrator without tracing. Debugging will be difficult.
Fix Action
Add tracing for agent invocations, state changes, and handoffs
Applies To
- *.ts
- *.js
Multi-Agent Without Timeout
Id
no-timeout-multi-agent
Severity
high
Type
regex
Pattern
await\s+this\.\w+Agent|await\s+agent\.|await\s+Promise\.all
Negative Pattern
timeout|AbortController|deadline|timeLimit
Message
Multi-agent execution without timeout. Could hang indefinitely.
Fix Action
Add execution timeout with Promise.race or AbortController
Applies To
- *.ts
- *.js
Hardcoded Agent Selection
Id
hardcoded-agent-selection
Severity
low
Type
regex
Pattern
if\s\([^)]===?\s["'][^"']+["']\s\)\s\{[^}]Agent
Message
Agent selection using hardcoded string matching. Consider using registry.
Fix Action
Use agent registry with capability matching for flexible routing
Applies To
- *.ts
- *.js
Swallowed Errors in Agent Chain
Id
no-error-propagation
Severity
high
Type
regex
Pattern
catch\s\([^)]\)\s\{[^}](?:console\.log|return\s+null|continue)
Message
Agent errors swallowed without propagation. May cause silent failures.
Fix Action
Propagate errors with context or implement explicit fallback handling
Applies To
- *.ts
- *.js
Agent Without Unique Identifier
Id
missing-agent-id
Severity
low
Type
regex
Pattern
class\s+\w+Agent\s*(?:extends|implements|{)
Negative Pattern
id:|agentId|this\.id\s*=
Message
Agent class without unique identifier. Tracing will be difficult.
Fix Action
Add unique agent ID for tracing and debugging
Applies To
- *.ts
- *.js
Global State Modification in Agent
Id
global-state-modification
Severity
high
Type
regex
Pattern
global\.|window\.|process\.env\s*=
Message
Agent modifying global state. Causes unpredictable behavior in multi-agent systems.
Fix Action
Use passed-in state object instead of global state
Applies To
- *.ts
- *.js