
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A powerful JavaScript SDK for creating and managing teams of AI agents with dependency management, parallel/sequential execution, and memory capabilities.
TeamAI is a powerful JavaScript SDK for creating, orchestrating, and managing teams of AI agents with advanced dependency management, conditional execution, parallel/sequential execution, session-based memory capabilities, and sophisticated tool integration.
shouldRun for intelligent agent execution based on runtime conditionsnpm install team-ai
For browser usage:
<script src="https://unpkg.com/team-ai@latest/dist/team-ai.umd.js"></script>
graph TB
A[TeamAI SDK] --> B[AgentTeam]
A --> C[Agent]
A --> D[AgentMemory]
A --> E[Tool Registry]
B --> F[Sequential Execution]
B --> G[Parallel Execution]
B --> H[Dependency Validation]
B --> I[Circular Detection]
B --> J[shouldRun Evaluation]
C --> K[OpenAI Integration]
C --> L[Tool Execution]
C --> M[Session Management]
C --> N[Placeholder Resolution]
C --> O[Conditional Logic]
D --> P[Session Isolation]
D --> Q[Token Management]
D --> R[Memory Cleanup]
D --> S[Conversation Storage]
E --> T[Global Tools]
E --> U[Agent-Specific Tools]
E --> V[Parameter Injection]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#e8f5e8
style D fill:#fff3e0
style E fill:#fce4ec
style J fill:#ffecb3
style O fill:#ffecb3
| Component | Purpose | Key Features |
|---|---|---|
| TeamAI | Main SDK orchestrator | Team management, global tools, execution coordination, performance monitoring |
| AgentTeam | Agent group manager | Dependency resolution, execution modes, workflow orchestration, parallel processing, conditional execution |
| Agent | Individual AI worker | Task execution, tool integration, session management, placeholder resolution, shouldRun logic |
| AgentMemory | Session-based storage | Per-session isolation, automatic cleanup, token limits, conversation history |
import { TeamAI } from 'team-ai';
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
const teamAI = new TeamAI();
const researcher = teamAI.createAgent({
name: 'market-researcher',
model: 'gpt-4',
llm: openai,
goal: 'Research comprehensive market data about {industry}',
task: 'Analyze market trends, key players, and opportunities in {industry}',
backHistory: 'You are a senior market research analyst with 10+ years of experience'
});
researcher.setSessionId('research-session-1');
const result = await researcher.execute({}, { industry: 'artificial intelligence' });
console.log(result.response);
const customerServiceTeam = teamAI.createTeam('customer-service', {
parallelMode: false
});
const router = teamAI.createAgent({
name: 'Router',
model: 'gpt-3.5-turbo',
llm: openai,
goal: 'Analyze customer message and determine intent',
task: 'Analyze: "{customerMessage}" and classify as: GREETING, QUESTION, COMPLAINT, or GOODBYE',
dependencies: []
});
const greetingAgent = teamAI.createAgent({
name: 'GreetingAgent',
model: 'gpt-3.5-turbo',
llm: openai,
goal: 'Handle customer greetings warmly',
task: 'Respond to greeting: "{customerMessage}"',
dependencies: ['Router'],
shouldRun: (deps, placeholders) => {
const routerResponse = deps.Router?.response || '';
return routerResponse.toLowerCase().includes('greeting') ||
routerResponse.toLowerCase().includes('hello') ||
routerResponse.toLowerCase().includes('hi');
}
});
const questionAgent = teamAI.createAgent({
name: 'QuestionAgent',
model: 'gpt-4',
llm: openai,
goal: 'Answer customer questions',
task: 'Answer: "{customerMessage}"',
dependencies: ['Router'],
shouldRun: (deps, placeholders) => {
const routerResponse = deps.Router?.response || '';
return routerResponse.toLowerCase().includes('question') ||
routerResponse.toLowerCase().includes('?') ||
routerResponse.toLowerCase().includes('how');
}
});
const complaintAgent = teamAI.createAgent({
name: 'ComplaintAgent',
model: 'gpt-4',
llm: openai,
goal: 'Handle complaints with empathy',
task: 'Address complaint: "{customerMessage}" with empathy and solutions',
dependencies: ['Router'],
shouldRun: (deps, placeholders) => {
const routerResponse = deps.Router?.response || '';
return routerResponse.toLowerCase().includes('complaint') ||
routerResponse.toLowerCase().includes('problem') ||
routerResponse.toLowerCase().includes('issue');
}
});
[router, greetingAgent, questionAgent, complaintAgent]
.forEach(agent => customerServiceTeam.addAgent(agent));
const testMessages = [
'Hello! How are you today?',
'How do I reset my password?',
'I have a problem with my order!',
'Hi! I have a question about pricing'
];
for (const message of testMessages) {
const result = await customerServiceTeam.execute({
customerMessage: message
});
console.log(`Message: "${message}"`);
console.log(`Executed: ${result.summary.successful} agents`);
console.log(`Skipped: ${result.skippedAgents.join(', ')}`);
}
flowchart TD
A[Start Team Execution] --> B[Validate Dependencies]
B --> C[Topological Sort Agents]
C --> D[Begin Agent Loop]
D --> E[Get Next Agent]
E --> F[Collect Dependency Results]
F --> G{Agent has shouldRun?}
G -->|No| H[Execute Agent]
G -->|Yes| I[Evaluate shouldRun Function]
I --> J{shouldRun Returns True?}
J -->|Yes| H[Execute Agent]
J -->|No| K[Skip Agent]
K --> L[Create Skipped Result]
L --> M[Add to Skipped List]
M --> N[Record in History]
H --> O[Agent Execution]
O --> P{Execution Success?}
P -->|Yes| Q[Store Success Result]
P -->|No| R[Store Error Result]
Q --> S[Mark as Completed]
R --> S
N --> T[Next Agent Check]
S --> T
T --> U{More Agents?}
U -->|Yes| E
U -->|No| V[Generate Summary]
V --> W[Return Results]
style I fill:#fff3c4
style J fill:#fff3c4
style K fill:#ffcdd2
style L fill:#ffcdd2
style M fill:#ffcdd2
style H fill:#c8e6c9
style O fill:#c8e6c9
The shouldRun function provides intelligent conditional execution for agents:
const agent = teamAI.createAgent({
name: 'conditional-agent',
dependencies: ['previous-agent'],
shouldRun: (deps, placeholders) => {
const previousResult = deps['previous-agent'];
return previousResult?.success && previousResult.data?.length > 0;
},
goal: 'Process data if previous agent succeeded',
task: 'Process the data: {previous-agent.response}'
});
| Parameter | Type | Description |
|---|---|---|
deps | Object | Results from all dependency agents with structure: { agentName: { success, response, tools, ... } } |
placeholders | Object | Global variables passed to team.execute(placeholders) |
Return Value: boolean - true to execute, false to skip
const dataProcessingTeam = teamAI.createTeam('data-processing');
const collector = teamAI.createAgent({
name: 'DataCollector',
goal: 'Collect data from source',
task: 'Collect data about: {topic}',
dependencies: [],
shouldUseTool: true
});
collector.registerTool('collect-data', async (params) => {
const success = Math.random() > 0.3;
if (!success) {
return {
success: false,
error: 'Data source unavailable',
needsUserInput: true
};
}
return {
success: true,
data: `Data about ${params.topic}`,
recordCount: 100
};
});
const validator = teamAI.createAgent({
name: 'DataValidator',
goal: 'Validate collected data',
task: 'Validate data quality',
dependencies: ['DataCollector'],
shouldRun: (deps, placeholders) => {
const collectorResult = deps.DataCollector;
if (!collectorResult?.success) {
console.log('โ ๏ธ DataValidator skipped: Collection failed');
return false;
}
const toolResult = collectorResult.tools?.['collect-data'];
if (!toolResult?.success) {
console.log('โ ๏ธ DataValidator skipped: No data to validate');
return false;
}
console.log('โ
DataValidator will run: Data collected successfully');
return true;
}
});
const processor = teamAI.createAgent({
name: 'DataProcessor',
goal: 'Process validated data',
task: 'Process the validated data',
dependencies: ['DataValidator'],
shouldRun: (deps, placeholders) => {
const validatorResult = deps.DataValidator;
if (validatorResult?.skipped) {
console.log('โ ๏ธ DataProcessor skipped: Validator was skipped');
return false;
}
if (!validatorResult?.success) {
console.log('โ ๏ธ DataProcessor skipped: Validation failed');
return false;
}
console.log('โ
DataProcessor will run: Data validated');
return true;
}
});
const userInputAgent = teamAI.createAgent({
name: 'UserInputAgent',
goal: 'Request user input when needed',
task: 'Request additional information from user',
dependencies: ['DataCollector'],
shouldRun: (deps, placeholders) => {
const collectorResult = deps.DataCollector;
if (collectorResult?.success) {
return false;
}
const toolResult = collectorResult?.tools?.['collect-data'];
return toolResult?.needsUserInput === true;
}
});
[collector, validator, processor, userInputAgent]
.forEach(agent => dataProcessingTeam.addAgent(agent));
const analysisTeam = teamAI.createTeam('analysis-system', {
parallelMode: true,
maxConcurrent: 3
});
const primaryAnalyzer = teamAI.createAgent({
name: 'PrimaryAnalyzer',
goal: 'Perform advanced analysis',
task: 'Analyze: {inputData}',
dependencies: [],
shouldUseTool: true
});
primaryAnalyzer.registerTool('advanced-analysis', async (params) => {
const shouldFail = Math.random() > 0.7;
if (shouldFail) {
return {
success: false,
error: 'Advanced analysis service unavailable',
fallbackNeeded: true
};
}
return {
success: true,
analysis: 'Advanced analysis results',
confidence: 0.95
};
});
const backupAnalyzer = teamAI.createAgent({
name: 'BackupAnalyzer',
goal: 'Perform backup analysis',
task: 'Fallback analysis for: {inputData}',
dependencies: ['PrimaryAnalyzer'],
shouldRun: (deps, placeholders) => {
const primaryResult = deps.PrimaryAnalyzer;
if (primaryResult?.success) {
console.log('โ ๏ธ BackupAnalyzer skipped: Primary succeeded');
return false;
}
const toolResult = primaryResult?.tools?.['advanced-analysis'];
if (toolResult?.fallbackNeeded) {
console.log('โ
BackupAnalyzer will run: Fallback needed');
return true;
}
return false;
}
});
const reportGenerator = teamAI.createAgent({
name: 'ReportGenerator',
goal: 'Generate analysis report',
task: 'Create report from available analysis',
dependencies: ['PrimaryAnalyzer', 'BackupAnalyzer'],
shouldRun: (deps, placeholders) => {
const primaryResult = deps.PrimaryAnalyzer;
const backupResult = deps.BackupAnalyzer;
const hasValidAnalysis = primaryResult?.success || backupResult?.success;
if (hasValidAnalysis) {
console.log('โ
ReportGenerator will run: Valid analysis available');
return true;
}
console.log('โ ๏ธ ReportGenerator skipped: No valid analysis');
return false;
}
});
[primaryAnalyzer, backupAnalyzer, reportGenerator]
.forEach(agent => analysisTeam.addAgent(agent));
const workflowTeam = teamAI.createTeam('permission-workflow');
const authChecker = teamAI.createAgent({
name: 'AuthChecker',
goal: 'Verify user authentication and permissions',
task: 'Check auth for user: {userId}',
dependencies: [],
shouldUseTool: true
});
authChecker.registerTool('check-auth', async (params) => {
const roles = ['admin', 'user', 'guest'];
const isAuth = Math.random() > 0.2;
return {
success: isAuth,
authenticated: isAuth,
role: isAuth ? roles[Math.floor(Math.random() * roles.length)] : null,
permissions: isAuth ? ['read', 'write', 'delete'].slice(0,
isAuth ? (Math.floor(Math.random() * 3) + 1) : 0) : []
};
});
const adminOps = teamAI.createAgent({
name: 'AdminOperations',
goal: 'Perform admin operations',
task: 'Execute admin task: {adminTask}',
dependencies: ['AuthChecker'],
shouldRun: (deps, placeholders) => {
const authResult = deps.AuthChecker;
if (!authResult?.success) {
console.log('โ ๏ธ AdminOps skipped: Auth failed');
return false;
}
const authTool = authResult.tools?.['check-auth'];
if (authTool?.role !== 'admin') {
console.log('โ ๏ธ AdminOps skipped: User not admin');
return false;
}
console.log('โ
AdminOps will run: Admin authenticated');
return true;
}
});
const userOps = teamAI.createAgent({
name: 'UserOperations',
goal: 'Perform user operations',
task: 'Execute user task: {userTask}',
dependencies: ['AuthChecker'],
shouldRun: (deps, placeholders) => {
const authResult = deps.AuthChecker;
if (!authResult?.success) {
console.log('โ ๏ธ UserOps skipped: Auth failed');
return false;
}
const authTool = authResult.tools?.['check-auth'];
const allowedRoles = ['user', 'admin'];
if (!allowedRoles.includes(authTool?.role)) {
console.log('โ ๏ธ UserOps skipped: Insufficient permissions');
return false;
}
console.log('โ
UserOps will run: User has permissions');
return true;
}
});
const guestOps = teamAI.createAgent({
name: 'GuestOperations',
goal: 'Perform guest operations',
task: 'Execute guest task: {guestTask}',
dependencies: ['AuthChecker'],
shouldRun: (deps, placeholders) => {
const authResult = deps.AuthChecker;
if (authResult?.skipped) {
console.log('โ ๏ธ GuestOps skipped: Auth check was skipped');
return false;
}
console.log('โ
GuestOps will run: Auth check completed');
return true;
}
});
[authChecker, adminOps, userOps, guestOps]
.forEach(agent => workflowTeam.addAgent(agent));
When agents are skipped via shouldRun, TeamAI provides comprehensive tracking:
const result = await team.execute(placeholders);
console.log(result);
/*
{
teamName: 'customer-service',
executionTime: 3500,
mode: 'sequential',
placeholders: { customerMessage: 'Hello!' },
results: {
Router: { success: true, response: '...', skipped: false },
GreetingAgent: { success: true, response: '...', skipped: false },
QuestionAgent: {
success: false,
response: 'Agent QuestionAgent was skipped: shouldRun condition not met',
skipped: true,
reason: 'shouldRun condition not met'
},
ComplaintAgent: {
success: false,
response: 'Agent ComplaintAgent was skipped: shouldRun condition not met',
skipped: true,
reason: 'shouldRun condition not met'
}
},
summary: {
total: 4,
successful: 2,
failed: 0,
skipped: 2,
successRate: 50.0,
executionRate: 50.0
},
skippedAgents: ['QuestionAgent', 'ComplaintAgent']
}
*/
const history = team.getExecutionHistory();
console.log(history);
/*
[
{
agent: 'Router',
timestamp: '2024-01-15T10:30:00.000Z',
result: { success: true, response: '...' },
placeholders: { customerMessage: 'Hello!' },
skipped: false
},
{
agent: 'GreetingAgent',
timestamp: '2024-01-15T10:30:02.000Z',
result: { success: true, response: '...' },
placeholders: { customerMessage: 'Hello!' },
skipped: false
},
{
agent: 'QuestionAgent',
timestamp: '2024-01-15T10:30:02.100Z',
result: { skipped: true, reason: 'shouldRun condition not met' },
placeholders: { customerMessage: 'Hello!' },
skipped: true,
reason: 'shouldRun condition not met'
}
]
*/
// โ
Good: Simple, fast evaluation
shouldRun: (deps, placeholders) => {
return deps.PreviousAgent?.success === true;
}
// โ Bad: Complex, slow evaluation
shouldRun: async (deps, placeholders) => {
const result = await someAsyncOperation();
return result.complexCalculation();
}
// โ
Good: Defensive programming
shouldRun: (deps, placeholders) => {
const depResult = deps.RequiredAgent;
if (!depResult || depResult.skipped) {
console.log('Dependency not available or was skipped');
return false;
}
return depResult.success && depResult.data?.length > 0;
}
// โ
Good: Clear decision logging
shouldRun: (deps, placeholders) => {
const authResult = deps.AuthChecker;
const userRole = authResult?.tools?.auth?.role;
if (userRole === 'admin') {
console.log('โ
AdminAgent will run: User is admin');
return true;
} else {
console.log(`โ ๏ธ AdminAgent skipped: User role is ${userRole || 'unknown'}`);
return false;
}
}
const parallelTeam = teamAI.createTeam('parallel-team', {
parallelMode: true,
maxConcurrent: 3
});
graph TB
A[AgentMemory] --> B[Global Memory]
A --> C[Session Manager]
C --> D[Session 1]
C --> E[Session 2]
C --> F[Session N]
D --> G[State Map]
D --> H[Metadata]
D --> I[Token Counter]
G --> J[Conversation History]
G --> K[Task Results]
G --> L[Context Data]
M[Cleanup Service] --> C
N[Token Limiter] --> D
N --> E
N --> F
style A fill:#e3f2fd
style C fill:#f3e5f5
style D fill:#e8f5e8
style M fill:#fff3e0
style N fill:#fce4ec
import { AgentMemory } from 'team-ai';
const memory = new AgentMemory(8000, {
maxSessions: 1000,
memoryTimeout: 3600000,
cleanupInterval: 600000
});
const sessionId = 'user-conversation-123';
memory.set('conversation_1', 'User asked about market trends', sessionId);
memory.set('analysis_result', 'Market shows 15% growth potential', sessionId);
const sessionData = memory.get(sessionId);
console.log('Session conversations:', sessionData);
memory.setSessionMetadata(sessionId, 'user_id', 'user123');
memory.setSessionMetadata(sessionId, 'domain', 'finance');
const results = memory.search('market trends', sessionId);
const sessionInfo = memory.getSessionInfo(sessionId);
console.log('Session info:', sessionInfo);
memory.cleanupExpiredSessions();
const exportedData = memory.exportSession(sessionId);
memory.importSession(exportedData);
Tools can also be conditionally executed based on agent state:
const smartAgent = teamAI.createAgent({
name: 'SmartAgent',
shouldUseTool: true,
goal: 'Smart data processing',
task: 'Process data intelligently based on conditions',
dependencies: ['DataSource']
});
smartAgent.registerTool('conditional-processor', async (params) => {
const sourceData = params.DataSource;
if (!sourceData?.success || !sourceData.data) {
return {
success: false,
error: 'No valid source data available',
skipped: true
};
}
return {
success: true,
processedData: processData(sourceData.data),
metadata: {
processingTime: Date.now(),
recordsProcessed: sourceData.data.length
}
};
});
const reportAgent = teamAI.createAgent({
name: 'ReportAgent',
goal: 'Generate report from processed data',
task: 'Create report from: {SmartAgent.tools.conditional-processor}',
dependencies: ['SmartAgent'],
shouldRun: (deps, placeholders) => {
const smartResult = deps.SmartAgent;
const toolResult = smartResult?.tools?.['conditional-processor'];
return toolResult?.success === true && !toolResult.skipped;
}
});
| Method | Parameters | Returns | Description |
|---|---|---|---|
execute(placeholders) | placeholders: Object | Promise<ExecutionResult> | Executes team with shouldRun evaluation |
getSkippedAgents() | - | string[] | Returns list of skipped agent names |
evaluateAgentShouldRun(agent, completed, placeholders) | agent: Agent, completed: Map, placeholders: Object | Object | Evaluates shouldRun condition |
| Method | Parameters | Returns | Description |
|---|---|---|---|
constructor(config) | config: AgentConfig | Agent | Creates agent with optional shouldRun |
interface AgentConfig {
name: string;
model?: string;
llm: OpenAI;
goal: string;
task: string;
backHistory?: string;
dependencies?: string[];
maxTokens?: number;
shouldUseTool?: boolean;
memory?: AgentMemory;
shouldRun?: (deps: DependencyResults, placeholders: PlaceholderObject) => boolean;
}
interface DependencyResults {
[agentName: string]: {
success: boolean;
response: string;
tools: Record<string, any>;
skipped?: boolean;
reason?: string;
};
}
interface PlaceholderObject {
[key: string]: any;
}
interface ExecutionResult {
teamName: string;
executionTime: number;
mode: 'sequential' | 'parallel';
placeholders: Object;
results: Record<string, AgentResult>;
summary: ExecutionSummary;
skippedAgents: string[];
}
interface AgentResult {
success: boolean;
response: string;
tools: Record<string, any>;
skipped?: boolean;
reason?: string;
}
interface ExecutionSummary {
total: number;
successful: number;
failed: number;
skipped: number;
successRate: number;
executionRate: number;
}
<!DOCTYPE html>
<html>
<head>
<title>TeamAI Browser Demo with shouldRun</title>
</head>
<body>
<script src="https://unpkg.com/team-ai@latest/dist/team-ai.umd.js"></script>
<script>
const teamAI = new TeamAI();
const userAgent = teamAI.createAgent({
name: 'user-detector',
model: 'gpt-3.5-turbo',
llm: openai,
goal: 'Detect if user is logged in',
task: 'Check user authentication status'
});
const authenticatedAgent = teamAI.createAgent({
name: 'authenticated-features',
model: 'gpt-3.5-turbo',
llm: openai,
goal: 'Show authenticated features',
task: 'Display user dashboard',
dependencies: ['user-detector'],
shouldRun: (deps, placeholders) => {
const userStatus = deps['user-detector']?.response || '';
return userStatus.includes('authenticated') || userStatus.includes('logged in');
}
});
userAgent.setSessionId(`browser-user-${Date.now()}`);
authenticatedAgent.setSessionId(`browser-auth-${Date.now()}`);
const browserTeam = teamAI.createTeam('browser-team');
browserTeam.addAgent(userAgent).addAgent(authenticatedAgent);
browserTeam.execute().then(result => {
console.log('Browser execution result:', result);
console.log('Skipped agents:', result.skippedAgents);
});
</script>
</body>
</html>
<script type="module">
import { TeamAI, AgentMemory } from 'https://unpkg.com/team-ai@latest/dist/team-ai.esm.js';
const teamAI = new TeamAI();
const pageAnalyzer = teamAI.createAgent({
name: 'page-analyzer',
memory: new AgentMemory(4000),
goal: 'Analyze current page',
task: 'Analyze page content and structure'
}).setSessionId('browser-session');
const modalAgent = teamAI.createAgent({
name: 'modal-trigger',
goal: 'Show modal if needed',
task: 'Display modal based on page analysis',
dependencies: ['page-analyzer'],
shouldRun: (deps, placeholders) => {
const analysis = deps['page-analyzer']?.response || '';
return analysis.includes('show-modal') || analysis.includes('call-to-action');
}
}).setSessionId('modal-session');
</script>
const complexAgent = teamAI.createAgent({
name: 'ComplexDecisionAgent',
dependencies: ['DataAgent', 'AuthAgent', 'ConfigAgent'],
shouldRun: (deps, placeholders) => {
const dataResult = deps.DataAgent;
const authResult = deps.AuthAgent;
const configResult = deps.ConfigAgent;
const hasValidData = dataResult?.success &&
dataResult.tools?.fetch?.recordCount > 0;
const isAuthorized = authResult?.success &&
authResult.tools?.auth?.permissions?.includes('process');
const isEnabled = configResult?.success &&
configResult.tools?.config?.features?.includes('advanced-processing');
const shouldExecute = hasValidData && isAuthorized && isEnabled;
console.log('๐ ComplexDecisionAgent shouldRun evaluation:');
console.log(` ๐ Has valid data: ${hasValidData}`);
console.log(` ๐ Is authorized: ${isAuthorized}`);
console.log(` โ๏ธ Is enabled: ${isEnabled}`);
console.log(` โ
Final decision: ${shouldExecute ? 'EXECUTE' : 'SKIP'}`);
return shouldExecute;
},
goal: 'Perform complex data processing',
task: 'Process data with advanced algorithms'
});
const parameterBasedAgent = teamAI.createAgent({
name: 'ParameterAgent',
dependencies: ['InputValidator'],
shouldRun: (deps, placeholders) => {
const userRole = placeholders.userRole;
const processMode = placeholders.mode;
const dataSize = placeholders.dataSize;
const validationResult = deps.InputValidator;
const isValidInput = validationResult?.success;
const hasPermission = ['admin', 'poweruser'].includes(userRole);
const isBatchMode = processMode === 'batch';
const isLargeDataset = dataSize > 1000;
const shouldExecute = isValidInput && hasPermission && isBatchMode && isLargeDataset;
if (!shouldExecute) {
const reasons = [];
if (!isValidInput) reasons.push('invalid input');
if (!hasPermission) reasons.push('insufficient permissions');
if (!isBatchMode) reasons.push('not batch mode');
if (!isLargeDataset) reasons.push('dataset too small');
console.log(`โ ๏ธ ParameterAgent skipped: ${reasons.join(', ')}`);
}
return shouldExecute;
},
goal: 'Process large datasets in batch mode',
task: 'Execute batch processing for {dataSize} records'
});
await team.execute({
userRole: 'admin',
mode: 'batch',
dataSize: 5000
});
await team.execute({
userRole: 'user',
mode: 'interactive',
dataSize: 100
});
const robustAgent = teamAI.createAgent({
name: 'RobustAgent',
dependencies: ['UnreliableAgent'],
shouldRun: (deps, placeholders) => {
try {
const depResult = deps.UnreliableAgent;
if (depResult?.skipped) {
console.log('โ ๏ธ RobustAgent: Dependency was skipped, proceeding with fallback');
return placeholders.allowFallback === true;
}
if (!depResult?.success) {
console.log('โ ๏ธ RobustAgent: Dependency failed, checking error type');
return depResult?.error?.includes('retryable');
}
return depResult.data?.isValid === true;
} catch (error) {
console.error('โ RobustAgent shouldRun error:', error.message);
return false;
}
},
goal: 'Handle unreliable dependencies gracefully',
task: 'Process data with error recovery'
});
graph TD
A[Start Sequential Execution] --> B[Get Sorted Agents]
B --> C[Begin Agent Loop]
C --> D[Get Next Agent]
D --> E[Collect Dependencies]
E --> F{Has shouldRun?}
F -->|No| G[Execute Agent]
F -->|Yes| H[Call shouldRun Function]
H --> I{Returns True?}
I -->|Yes| G[Execute Agent]
I -->|No| J[Skip Agent]
J --> K[Create Skipped Result]
K --> L[Mark as Skipped]
L --> M[Continue to Next]
G --> N[Execute Agent Logic]
N --> O{Success?}
O -->|Yes| P[Store Success]
O -->|No| Q[Store Error]
P --> R[Mark Completed]
Q --> R
M --> S{More Agents?}
R --> S
S -->|Yes| D
S -->|No| T[Generate Final Summary]
T --> U[Return Results with Skip Info]
style H fill:#fff3c4
style I fill:#fff3c4
style J fill:#ffcdd2
style K fill:#ffcdd2
style L fill:#ffcdd2
graph TD
A[Start Parallel Execution] --> B[Initialize State]
B --> C[Main Execution Loop]
C --> D[Find Ready Agents]
D --> E[Filter by Dependencies]
E --> F[Check shouldRun for Each]
F --> G[Split into Execute/Skip Groups]
G --> H[Create Skip Results]
G --> I[Execute Batch]
H --> J[Update Skipped Set]
I --> K[Parallel Agent Execution]
K --> L{All Batch Complete?}
L -->|No| M[Wait for Completion]
M --> L
L -->|Yes| N[Update Completed Set]
N --> O{More Agents Available?}
O -->|Yes| D
O -->|No| P[Check Remaining]
P --> Q{Unprocessed Agents?}
Q -->|Yes| R[Mark as Dependency Failed]
Q -->|No| S[Generate Summary]
R --> S
S --> T[Return Results]
style F fill:#fff3c4
style G fill:#fff3c4
style H fill:#ffcdd2
style J fill:#ffcdd2
class shouldRunMonitor {
constructor(teamAI) {
this.teamAI = teamAI;
this.shouldRunHistory = new Map();
this.decisionLog = [];
}
wrapTeamForShouldRunMonitoring(team) {
const originalEvaluate = team.evaluateAgentShouldRun.bind(team);
team.evaluateAgentShouldRun = (agent, completed, placeholders) => {
const startTime = Date.now();
const result = originalEvaluate(agent, completed, placeholders);
const evaluationTime = Date.now() - startTime;
const decision = {
agentName: agent.name,
shouldRun: result.shouldRun,
reason: result.reason,
evaluationTime,
timestamp: new Date().toISOString(),
dependencyStates: Object.keys(result.dependencyResults).reduce((acc, key) => {
acc[key] = {
success: result.dependencyResults[key]?.success,
skipped: result.dependencyResults[key]?.skipped
};
return acc;
}, {}),
placeholders: { ...placeholders }
};
this.decisionLog.push(decision);
if (!this.shouldRunHistory.has(agent.name)) {
this.shouldRunHistory.set(agent.name, []);
}
this.shouldRunHistory.get(agent.name).push(decision);
console.log(`๐ฏ shouldRun Decision for ${agent.name}:`);
console.log(` Decision: ${result.shouldRun ? 'โ
EXECUTE' : 'โญ๏ธ SKIP'}`);
console.log(` Reason: ${result.reason || 'Condition met'}`);
console.log(` Evaluation time: ${evaluationTime}ms`);
console.log(` Dependencies: ${Object.keys(result.dependencyResults).length}`);
return result;
};
return team;
}
getShouldRunStats() {
const stats = {
totalDecisions: this.decisionLog.length,
executionDecisions: this.decisionLog.filter(d => d.shouldRun).length,
skipDecisions: this.decisionLog.filter(d => !d.shouldRun).length,
averageEvaluationTime: 0,
agentStats: new Map()
};
if (this.decisionLog.length > 0) {
stats.averageEvaluationTime = this.decisionLog.reduce((sum, d) => sum + d.evaluationTime, 0) / this.decisionLog.length;
}
for (const [agentName, decisions] of this.shouldRunHistory) {
const agentExecutions = decisions.filter(d => d.shouldRun).length;
const agentSkips = decisions.filter(d => !d.shouldRun).length;
stats.agentStats.set(agentName, {
totalDecisions: decisions.length,
executions: agentExecutions,
skips: agentSkips,
executionRate: (agentExecutions / decisions.length) * 100,
averageEvaluationTime: decisions.reduce((sum, d) => sum + d.evaluationTime, 0) / decisions.length,
commonSkipReasons: this.getCommonSkipReasons(decisions.filter(d => !d.shouldRun))
});
}
return stats;
}
getCommonSkipReasons(skipDecisions) {
const reasonCounts = {};
skipDecisions.forEach(decision => {
const reason = decision.reason || 'Unknown';
reasonCounts[reason] = (reasonCounts[reason] || 0) + 1;
});
return Object.entries(reasonCounts)
.sort(([,a], [,b]) => b - a)
.slice(0, 5)
.map(([reason, count]) => ({ reason, count }));
}
printShouldRunReport() {
const stats = this.getShouldRunStats();
console.log('\n๐ shouldRun Execution Report');
console.log('='.repeat(50));
console.log(`๐ Total Decisions: ${stats.totalDecisions}`);
console.log(`โ
Executions: ${stats.executionDecisions} (${((stats.executionDecisions/stats.totalDecisions)*100).toFixed(1)}%)`);
console.log(`โญ๏ธ Skips: ${stats.skipDecisions} (${((stats.skipDecisions/stats.totalDecisions)*100).toFixed(1)}%)`);
console.log(`โก Avg Evaluation Time: ${stats.averageEvaluationTime.toFixed(2)}ms`);
console.log('\n๐ค Per-Agent Statistics:');
for (const [agentName, agentStats] of stats.agentStats) {
console.log(`\n ${agentName}:`);
console.log(` Decisions: ${agentStats.totalDecisions}`);
console.log(` Execution Rate: ${agentStats.executionRate.toFixed(1)}%`);
console.log(` Avg Eval Time: ${agentStats.averageEvaluationTime.toFixed(2)}ms`);
if (agentStats.commonSkipReasons.length > 0) {
console.log(` Common Skip Reasons:`);
agentStats.commonSkipReasons.forEach(({ reason, count }) => {
console.log(` - ${reason}: ${count}x`);
});
}
}
}
exportDecisionLog() {
return {
metadata: {
exportTime: new Date().toISOString(),
totalDecisions: this.decisionLog.length,
version: '1.0'
},
decisions: this.decisionLog,
stats: this.getShouldRunStats()
};
}
}
const monitor = new shouldRunMonitor(teamAI);
const monitoredTeam = monitor.wrapTeamForShouldRunMonitoring(customerServiceTeam);
const result = await monitoredTeam.execute({ customerMessage: 'Hello!' });
monitor.printShouldRunReport();
const decisionData = monitor.exportDecisionLog();
console.log('Decision log exported:', decisionData.metadata);
class RobustTeamExecution {
constructor(teamAI) {
this.teamAI = teamAI;
this.errorLog = new Map();
this.shouldRunFailures = new Map();
}
async executeWithShouldRunRecovery(teamName, placeholders, options = {}) {
const {
maxRetries = 3,
timeout = 300000,
sessionPrefix = 'robust-session',
shouldRunRetryPolicy = 'skip'
} = options;
const team = this.teamAI.getTeam(teamName);
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`๐ Attempt ${attempt}/${maxRetries} for team ${teamName}`);
this.wrapShouldRunForErrorHandling(team, shouldRunRetryPolicy);
this.setupRetrySessionIds(team, sessionPrefix, attempt);
const result = await this.executeWithTimeout(team, placeholders, timeout);
console.log(`โ
Team ${teamName} succeeded on attempt ${attempt}`);
this.logShouldRunStats(result);
return result;
} catch (error) {
this.logError(teamName, attempt, error);
if (attempt === maxRetries) {
throw new Error(`Team ${teamName} failed after ${maxRetries} attempts: ${error.message}`);
}
this.cleanupFailedAttempt(team);
await this.wait(Math.pow(2, attempt) * 1000);
}
}
}
wrapShouldRunForErrorHandling(team, retryPolicy) {
team.agents.forEach((agent, name) => {
if (agent.shouldRun && typeof agent.shouldRun === 'function') {
const originalShouldRun = agent.shouldRun;
agent.shouldRun = (deps, placeholders) => {
try {
const result = originalShouldRun(deps, placeholders);
if (!this.shouldRunFailures.has(name)) {
this.shouldRunFailures.set(name, { successes: 0, failures: 0 });
}
this.shouldRunFailures.get(name).successes++;
return result;
} catch (error) {
console.error(`โ shouldRun error for agent ${name}:`, error.message);
if (!this.shouldRunFailures.has(name)) {
this.shouldRunFailures.set(name, { successes: 0, failures: 0 });
}
this.shouldRunFailures.get(name).failures++;
switch (retryPolicy) {
case 'skip':
console.log(`โญ๏ธ Skipping agent ${name} due to shouldRun error`);
return false;
case 'retry':
console.log(`๐ Retrying shouldRun for agent ${name}`);
try {
return originalShouldRun(deps, placeholders);
} catch (retryError) {
console.error(`โ shouldRun retry failed for ${name}:`, retryError.message);
return false;
}
case 'fallback':
console.log(`๐ก๏ธ Using fallback logic for agent ${name}`);
return Object.values(deps).every(dep => dep?.success);
default:
return false;
}
}
};
}
});
}
logShouldRunStats(result) {
const skippedCount = result.skippedAgents.length;
const executedCount = result.summary.successful + result.summary.failed;
const totalCount = result.summary.total;
console.log('\n๐ shouldRun Execution Summary:');
console.log(` ๐ฏ Total Agents: ${totalCount}`);
console.log(` โ
Executed: ${executedCount}`);
console.log(` โญ๏ธ Skipped: ${skippedCount}`);
console.log(` ๐ Execution Rate: ${((executedCount/totalCount)*100).toFixed(1)}%`);
if (this.shouldRunFailures.size > 0) {
console.log('\n๐จ shouldRun Error Summary:');
for (const [agentName, stats] of this.shouldRunFailures) {
const totalEvals = stats.successes + stats.failures;
const errorRate = (stats.failures / totalEvals) * 100;
if (stats.failures > 0) {
console.log(` โ ๏ธ ${agentName}: ${stats.failures}/${totalEvals} evaluations failed (${errorRate.toFixed(1)}%)`);
}
}
}
}
}
class InputValidator {
static validateShouldRunFunction(shouldRunFn, agentName) {
if (!shouldRunFn) return true;
if (typeof shouldRunFn !== 'function') {
throw new Error(`Agent ${agentName}: shouldRun must be a function`);
}
try {
const testResult = shouldRunFn({}, {});
if (typeof testResult !== 'boolean') {
console.warn(`โ ๏ธ Agent ${agentName}: shouldRun should return boolean, got ${typeof testResult}`);
}
} catch (error) {
console.warn(`โ ๏ธ Agent ${agentName}: shouldRun function validation failed:`, error.message);
}
return true;
}
static validateDependencyAccess(shouldRunFn, agentName, dependencies) {
if (!shouldRunFn || dependencies.length === 0) return true;
const mockDeps = {};
dependencies.forEach(dep => {
mockDeps[dep] = {
success: true,
response: 'mock response',
tools: {},
skipped: false
};
});
try {
shouldRunFn(mockDeps, {});
console.log(`โ
Agent ${agentName}: shouldRun dependency access validated`);
} catch (error) {
console.warn(`โ ๏ธ Agent ${agentName}: shouldRun dependency access issue:`, error.message);
}
return true;
}
}
const createValidatedAgent = (teamAI, config) => {
if (config.shouldRun) {
InputValidator.validateShouldRunFunction(config.shouldRun, config.name);
InputValidator.validateDependencyAccess(config.shouldRun, config.name, config.dependencies || []);
}
return teamAI.createAgent(config);
};
const validatedAgent = createValidatedAgent(teamAI, {
name: 'ValidatedAgent',
dependencies: ['DataAgent'],
shouldRun: (deps, placeholders) => {
const dataResult = deps.DataAgent;
return dataResult?.success === true;
},
goal: 'Process validated data',
task: 'Handle data processing with validation'
});
class ShouldRunCache {
constructor(maxSize = 1000, ttl = 300000) {
this.cache = new Map();
this.maxSize = maxSize;
this.ttl = ttl;
}
generateCacheKey(agentName, deps, placeholders) {
const depsHash = this.hashObject(deps);
const placeholdersHash = this.hashObject(placeholders);
return `${agentName}:${depsHash}:${placeholdersHash}`;
}
hashObject(obj) {
return btoa(JSON.stringify(obj)).slice(0, 16);
}
get(key) {
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() - entry.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
entry.hits++;
return entry.value;
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, {
value,
timestamp: Date.now(),
hits: 0
});
}
getStats() {
const entries = Array.from(this.cache.values());
return {
size: this.cache.size,
totalHits: entries.reduce((sum, entry) => sum + entry.hits, 0),
avgAge: entries.length > 0 ?
(Date.now() - entries.reduce((sum, entry) => sum + entry.timestamp, 0) / entries.length) / 1000 : 0
};
}
}
function createCachedShouldRun(originalShouldRun, agentName, cache) {
return (deps, placeholders) => {
const cacheKey = cache.generateCacheKey(agentName, deps, placeholders);
const cached = cache.get(cacheKey);
if (cached !== null) {
console.log(`๐ shouldRun cache hit for ${agentName}`);
return cached;
}
const result = originalShouldRun(deps, placeholders);
cache.set(cacheKey, result);
return result;
};
}
const shouldRunCache = new ShouldRunCache();
const cachedAgent = teamAI.createAgent({
name: 'CachedAgent',
dependencies: ['DataAgent'],
shouldRun: createCachedShouldRun(
(deps, placeholders) => {
console.log('๐ Performing expensive shouldRun evaluation...');
return deps.DataAgent?.success && placeholders.enableProcessing;
},
'CachedAgent',
shouldRunCache
),
goal: 'Process with cached shouldRun',
task: 'Handle cached decision making'
});
class ShouldRunTestSuite {
constructor() {
this.tests = [];
this.results = [];
}
addShouldRunTest(description, shouldRunFn, testCases) {
this.tests.push({
description,
shouldRunFn,
testCases
});
}
async runAllTests() {
console.log(`\n๐งช Running shouldRun Tests...`);
console.log('='.repeat(50));
for (const test of this.tests) {
await this.runShouldRunTest(test);
}
this.printTestSummary();
}
async runShouldRunTest(test) {
console.log(`\n๐ Testing: ${test.description}`);
let passed = 0;
let failed = 0;
for (const testCase of test.testCases) {
try {
const result = test.shouldRunFn(testCase.deps, testCase.placeholders);
if (result === testCase.expected) {
console.log(` โ
${testCase.name}: Expected ${testCase.expected}, got ${result}`);
passed++;
} else {
console.log(` โ ${testCase.name}: Expected ${testCase.expected}, got ${result}`);
failed++;
}
} catch (error) {
console.log(` ๐ฅ ${testCase.name}: Error - ${error.message}`);
failed++;
}
}
const testResult = {
description: test.description,
passed,
failed,
total: test.testCases.length
};
this.results.push(testResult);
console.log(`๐ Result: ${passed}/${passed + failed} passed`);
}
printTestSummary() {
const totalPassed = this.results.reduce((sum, r) => sum + r.passed, 0);
const totalFailed = this.results.reduce((sum, r) => sum + r.failed, 0);
const totalTests = totalPassed + totalFailed;
console.log('\n๐ shouldRun Test Summary:');
console.log('='.repeat(30));
console.log(`Total Tests: ${totalTests}`);
console.log(`Passed: ${totalPassed}`);
console.log(`Failed: ${totalFailed}`);
console.log(`Pass Rate: ${((totalPassed / totalTests) * 100).toFixed(1)}%`);
if (totalFailed > 0) {
console.log('\nโ Failed Test Suites:');
this.results.filter(r => r.failed > 0).forEach(result => {
console.log(` - ${result.description}: ${result.failed} failures`);
});
}
}
}
const testSuite = new ShouldRunTestSuite();
testSuite.addShouldRunTest(
'Basic dependency success check',
(deps, placeholders) => {
return deps.DataCollector?.success === true;
},
[
{
name: 'Success case',
deps: { DataCollector: { success: true, response: 'data collected' } },
placeholders: {},
expected: true
},
{
name: 'Failure case',
deps: { DataCollector: { success: false, response: 'failed' } },
placeholders: {},
expected: false
},
{
name: 'Missing dependency',
deps: {},
placeholders: {},
expected: false
}
]
);
testSuite.addShouldRunTest(
'Multi-condition validation',
(deps, placeholders) => {
const authResult = deps.AuthChecker;
const dataResult = deps.DataCollector;
return authResult?.success &&
authResult.tools?.auth?.role === 'admin' &&
dataResult?.success &&
placeholders.mode === 'production';
},
[
{
name: 'All conditions met',
deps: {
AuthChecker: {
success: true,
tools: { auth: { role: 'admin' } }
},
DataCollector: { success: true }
},
placeholders: { mode: 'production' },
expected: true
},
{
name: 'Wrong user role',
deps: {
AuthChecker: {
success: true,
tools: { auth: { role: 'user' } }
},
DataCollector: { success: true }
},
placeholders: { mode: 'production' },
expected: false
},
{
name: 'Development mode',
deps: {
AuthChecker: {
success: true,
tools: { auth: { role: 'admin' } }
},
DataCollector: { success: true }
},
placeholders: { mode: 'development' },
expected: false
}
]
);
testSuite.addShouldRunTest(
'Tool result validation',
(deps, placeholders) => {
const collectorResult = deps.DataCollector;
const toolResult = collectorResult?.tools?.['data-fetch'];
return toolResult?.success && toolResult.recordCount > 0;
},
[
{
name: 'Valid tool result',
deps: {
DataCollector: {
success: true,
tools: {
'data-fetch': {
success: true,
recordCount: 100
}
}
}
},
placeholders: {},
expected: true
},
{
name: 'Empty dataset',
deps: {
DataCollector: {
success: true,
tools: {
'data-fetch': {
success: true,
recordCount: 0
}
}
}
},
placeholders: {},
expected: false
},
{
name: 'Tool failed',
deps: {
DataCollector: {
success: true,
tools: {
'data-fetch': {
success: false,
error: 'API timeout'
}
}
}
},
placeholders: {},
expected: false
}
]
);
testSuite.runAllTests();
Keep Functions Pure and Deterministic
// โ
Good: Pure function
shouldRun: (deps, placeholders) => {
return deps.DataAgent?.success && placeholders.enableProcessing;
}
// โ Bad: Side effects and non-deterministic
shouldRun: (deps, placeholders) => {
console.log('Deciding...');
return Math.random() > 0.5;
}
Handle Edge Cases Gracefully
shouldRun: (deps, placeholders) => {
const dep = deps.RequiredAgent;
if (!dep) return false;
if (dep.skipped) return false;
if (!dep.success) return false;
return dep.data?.isValid === true;
}
Use Meaningful Variable Names
// โ
Good: Clear and descriptive
shouldRun: (deps, placeholders) => {
const authenticationResult = deps.AuthService;
const hasValidPermissions = authenticationResult?.tools?.auth?.permissions?.includes('write');
const isProductionMode = placeholders.environment === 'production';
return hasValidPermissions && isProductionMode;
}
Document Complex Logic
shouldRun: (deps, placeholders) => {
const dataResult = deps.DataCollector;
const authResult = deps.AuthService;
// Only process data if:
// 1. Data collection succeeded
// 2. User is authenticated
// 3. Dataset is not empty
// 4. Processing is enabled in config
return dataResult?.success &&
authResult?.success &&
dataResult.tools?.fetch?.recordCount > 0 &&
placeholders.enableProcessing === true;
}
If you're upgrading from static agent workflows to shouldRun-based conditional execution:
// BEFORE: Static workflow
const oldTeam = teamAI.createTeam('old-workflow');
const processor = teamAI.createAgent({
name: 'DataProcessor',
dependencies: ['DataCollector'],
goal: 'Process all collected data',
task: 'Process data regardless of quality'
});
// AFTER: Conditional workflow with shouldRun
const newTeam = teamAI.createTeam('new-workflow');
const smartProcessor = teamAI.createAgent({
name: 'SmartDataProcessor',
dependencies: ['DataCollector'],
shouldRun: (deps, placeholders) => {
const collectorResult = deps.DataCollector;
const quality = collectorResult?.tools?.validator?.qualityScore || 0;
return collectorResult?.success && quality >= 0.8;
},
goal: 'Process high-quality data only',
task: 'Process validated, high-quality data'
});
const fallbackProcessor = teamAI.createAgent({
name: 'FallbackProcessor',
dependencies: ['DataCollector'],
shouldRun: (deps, placeholders) => {
const collectorResult = deps.DataCollector;
const quality = collectorResult?.tools?.validator?.qualityScore || 0;
return collectorResult?.success && quality < 0.8 && quality >= 0.5;
},
goal: 'Handle medium-quality data',
task: 'Process data with additional validation'
});
ISC License - see LICENSE file for details.
FAQs
A powerful JavaScript SDK for creating and managing teams of AI agents with dependency management, parallel/sequential execution, and memory capabilities.
The npm package team-ai receives a total of 0 weekly downloads. As such, team-ai popularity was classified as not popular.
We found that team-ai demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.