
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.
@itaylor/agentic-team
Advanced tools
A library for coordinating teams of AI agents working together toward a goal
A library for coordinating teams of AI agents working together toward a shared goal.
@itaylor/agentic-team provides coordination primitives for managing a team of AI agents, where a manager agent delegates tasks to team members and coordinates their work. It builds on @itaylor/agentic-loop to provide multi-agent orchestration with task management, inter-agent communication, and automatic task queueing.
It's my belief that the best Agentic Coding UI may not be the terminal or the IDE, but something else that we haven't seen yet. Whatever that other UI paradigm may be, there's a need to have an agent that oversees the work of other agents, and coordinates their work towards a shared goal. This is an attempt to implement that pattern that is detached from any particular UI or specific sets of team rules that we may wish to enforce.
npm install @itaylor/agentic-team @itaylor/agentic-loop
You'll also need an AI SDK provider package, e.g.:
npm install @ai-sdk/anthropic
# or @ai-sdk/openai, @ai-sdk/google, ai-sdk-ollama, etc.
import { createAgentTeam } from '@itaylor/agentic-team';
import { anthropic } from '@ai-sdk/anthropic';
// Create a team
const team = createAgentTeam({
teamId: 'project-001',
goal: 'Implement user authentication feature',
modelConfig: {
languageModel: anthropic('claude-opus-4-5'),
},
manager: {
id: 'Morgan#1',
role: 'manager',
// systemPrompt is optional — defaults to a generic manager prompt
// The library provides the manager with goal context and workflow instructions automatically
},
team: [
{
id: 'Bailey#1',
role: 'backend_engineer',
// systemPrompt is optional — defaults to a role-based prompt
},
{
id: 'Alex#1',
role: 'frontend_engineer',
}
],
callbacks: {
onTaskCreated: (task) => console.log('Task created:', task.id),
onTaskCompleted: (task) => console.log('Task completed:', task.id),
onGoalComplete: (summary) => console.log('Goal complete:', summary),
}
});
// Run the team autonomously until goal is complete
const result = await team.run();
console.log('Goal complete:', result.complete);
console.log('Iterations:', result.iterations);
Tasks have a lifecycle:
task_completeAgents work on one task at a time. Additional tasks are queued automatically.
Agents communicate via ask and tell:
When an agent calls ask, their session suspends (using agentic-loop's suspension mechanism). When a reply is delivered, the agent can be resumed.
The manager is the sole gateway for external communication. When a worker needs information from an outside party, it asks the manager, who then decides whether to answer from its own knowledge or relay the question externally via ask. This keeps the manager fully informed and prevents workers from independently contacting external entities.
The manager can also suspend by calling wait_for_task_completions() after assigning work. This prevents the manager from looping unnecessarily while waiting for team members to finish. The manager automatically resumes when task completion notifications arrive.
Manager Tools:
assign_task(assignee, title, brief) - Create and assign a taskwait_for_task_completions() - Wait for assigned tasks to complete (suspends)check_team_status() - See all agents and tasksask(to, question) - Ask agent or external entity (suspends)tell(to, message, inReplyTo?) - Send messagetask_complete(summary) - Complete the overall goal (built-in from agentic-loop)Team Member Tools:
get_task_brief() - Re-read current task detailsask(question) - Ask the manager a question (suspends; manager answers or escalates externally)tell(to, message, inReplyTo?) - Send messagetask_complete(summary) - Complete current task (built-in from agentic-loop)createAgentTeam(config)Creates a new agent team coordinator.
Config:
{
teamId: string; // Unique team identifier
goal: string; // Overall goal the team is working toward
modelConfig: ModelConfig; // LLM configuration
manager: ManagerConfig; // Manager agent configuration
team: TeamMember[]; // Team member configurations
resumeFrom?: AgentTeamState; // Resume from previous state (for crash recovery)
callbacks?: TeamCallbacks; // Event callbacks for persistence
logger?: Logger; // Custom logger
maxTurnsPerSession?: number; // Max turns per agent run (default: 50)
tokenLimit?: number; // Token limit for summarization
}
ModelConfig:
{
languageModel: LanguageModel; // Any AI SDK LanguageModelV3 instance
languageModelSettings?: LanguageModelSettings; // Applied to every agent LLM call
summaryLanguageModel?: LanguageModel; // Optional separate model for summarization
summaryLanguageModelSettings?: LanguageModelSettings; // Settings for summary model only
}
When summaryLanguageModel is not set, summarization uses languageModel + languageModelSettings. When it is set, summaryLanguageModelSettings is fully independent — no fallback from the main settings.
// Cheap fast model for summarization, expensive model for agent work
modelConfig: {
languageModel: anthropic('claude-opus-4-5'),
summaryLanguageModel: anthropic('claude-haiku-4-5'),
summaryLanguageModelSettings: { temperature: 0 },
}
TeamMember / ManagerConfig:
{
id: string; // Unique agent identifier (e.g. "Bailey#1")
role: string; // Role label (e.g. "backend_engineer")
systemPrompt?: string; // Optional — library provides a default if omitted
tools?: Record<string, Tool>; // Domain-specific tools (coordination tools added automatically)
modelConfig?: ModelConfig; // Optional — overrides the top-level modelConfig for this agent
}
If you want to give your agents code editing capabilities (read/write files, search, apply patches, etc.), consider using agent-mcp to supply tools via the MCP stdio protocol:
import { Experimental_StdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio";
import { createMCPClient } from "@ai-sdk/mcp";
const transport = new Experimental_StdioMCPTransport({
command: "/path/to/agent-mcp",
args: ["/path/to/your/repo"],
});
const mcpClient = await createMCPClient({ transport });
const mcpTools = await mcpClient.tools();
const team = createAgentTeam({
// ...
team: [
{
id: "Bailey#1",
role: "backend_engineer",
tools: mcpTools, // agent-mcp tools merged with built-in coordination tools
},
],
});
await team.run();
await mcpClient.close();
Returns: AgentTeam object
team.run()Run the team autonomously until the goal is complete or agents are blocked waiting for external input (like BigBoss replies).
Returns: Promise<{ complete: boolean, blockedAgents: Array<{ agentId, messageId }>, iterations: number }>
This is the primary way to use the library - just call run() and the team coordinates itself automatically.
team.runAgent(agentId)Run an agent (manager or team member). The agent will work on their current task or process messages.
Returns: Promise<AgentRunResult>
{
agentId: string;
completed?: boolean; // True if task completed
suspended?: boolean; // True if agent blocked
suspendInfo?: { reason, data }; // Suspension details
finalOutput: string; // Agent's final message
completionReason: string; // How session ended
}
team.getNextWork()Get agents that have active tasks ready to work on.
Returns: WorkItem[]
{
agentId: string;
taskId: string;
task: Task;
}
team.getBlockedAgents()Get agents that are blocked waiting for message replies.
Returns: Array<{ agentId, messageId }>
team.deliverMessageReply(messageId, replyContent)Deliver a reply to a message, unblocking the waiting agent.
Returns: string | null - The agent ID that should be resumed, or null if not found
team.isGoalComplete()Check if the overall goal has been completed (manager called task_complete).
Returns: boolean
All callbacks are optional and can be used for persistence, logging, or UI updates:
{
onTaskCreated: (task: Task) => void;
onTaskActivated: (task: Task) => void;
onTaskCompleted: (task: Task) => void;
onMessageSent: (message: TeamMessage) => void;
onMessageDelivered: (message: TeamMessage) => void;
onAgentBlocked: (agentId: string, messageId: string) => void;
onAgentUnblocked: (agentId: string) => void;
onGoalComplete: (summary: string) => void;
onStateChange: (state: AgentTeamState) => void;
}
The library is designed to be stateless - all state is in the AgentTeamState object which can be serialized and restored:
// Save state
const state = team.state;
await fs.writeFile('team-state.json', JSON.stringify(state));
// Resume later (even after server restart)
const savedState = JSON.parse(await fs.readFile('team-state.json'));
const team = createAgentTeam({
...config,
resumeFrom: savedState
});
Note: The agentStates Map needs special handling for JSON serialization:
// Serialize
const stateForSave = {
...state,
agentStates: Array.from(state.agentStates.entries())
};
// Deserialize
const loaded = JSON.parse(savedData);
const state = {
...loaded,
agentStates: new Map(loaded.agentStates)
};
import { createAgentTeam } from '@itaylor/agentic-team';
import { anthropic } from '@ai-sdk/anthropic';
const team = createAgentTeam({
teamId: 'feature-auth',
goal: 'Implement user authentication with login and signup',
modelConfig: { languageModel: anthropic('claude-opus-4-5') },
manager: {
id: 'Morgan#1',
role: 'manager',
// No systemPrompt needed — the library provides goal context and workflow instructions
},
team: [
{ id: 'Bailey#1', role: 'backend_engineer' },
{ id: 'Alex#1', role: 'frontend_engineer' }
]
});
// Run the team autonomously
const result = await team.run();
if (result.complete) {
console.log('Goal complete!', team.state.goalSummary);
console.log('Completed in', result.iterations, 'iterations');
} else if (result.blockedAgents.length > 0) {
console.log('Blocked on external input:', result.blockedAgents);
// Handle external questions (e.g., from BigBoss via UI)
// Then call team.run() again to resume
}
When an agent asks an external entity (like "BigBoss"), team.run() returns with blocked agents:
// Run team
const result = await team.run();
if (!result.complete && result.blockedAgents.length > 0) {
for (const blocked of result.blockedAgents) {
const message = team.state.messages.find(m => m.id === blocked.messageId);
if (message && message.to === 'BigBoss') {
// Get answer from human (via UI, CLI, etc.)
const humanAnswer = await promptHuman(message.content);
// Deliver reply
team.deliverMessageReply(blocked.messageId, humanAnswer);
}
}
// Resume team
const resumedResult = await team.run();
}
MIT
FAQs
A library for coordinating teams of AI agents working together toward a goal
We found that @itaylor/agentic-team 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.