agent-frameworkai-agentsanthropicclaudegemma4llmmodel-agnosticmulti-agentnodejsollamaopenaiorchestrationstructured-outputtask-schedulingtool-usetypescript
isTaskReady() rejects non-pending tasks on its first line, but unblockDependents() passed blocked tasks directly to it. This meant dependent tasks stayed blocked forever after their dependencies completed, breaking any workflow with task dependencies. Fix: pass a pending-status copy so isTaskReady only checks the dependency condition. |
||
|---|---|---|
| examples | ||
| src | ||
| .gitignore | ||
| LICENSE | ||
| README.md | ||
| README_zh.md | ||
| package-lock.json | ||
| package.json | ||
| tsconfig.json | ||
README.md
Open Multi-Agent
Build AI agent teams that decompose goals into tasks automatically. Define agents with roles and tools, describe a goal — the framework plans the task graph, schedules dependencies, and runs everything in parallel.
3 runtime dependencies. 20 source files. One runTeam() call from goal to result.
English | 中文
Why Open Multi-Agent?
- Auto Task Decomposition — Describe a goal in plain text. A built-in coordinator agent breaks it into a task DAG with dependencies and assignees — no manual orchestration needed.
- Multi-Agent Teams — Define agents with different roles, tools, and even different models. They collaborate through a message bus and shared memory.
- Task DAG Scheduling — Tasks have dependencies. The framework resolves them topologically — dependent tasks wait, independent tasks run in parallel.
- Model Agnostic — Claude, GPT, and local models (Ollama, vLLM, LM Studio) in the same team. Swap models per agent via
baseURL. - In-Process Execution — No subprocess overhead. Everything runs in one Node.js process. Deploy to serverless, Docker, CI/CD.
Quick Start
Requires Node.js >= 18.
npm install @jackchen_me/open-multi-agent
Set ANTHROPIC_API_KEY (and optionally OPENAI_API_KEY or GITHUB_TOKEN for Copilot) in your environment.
Three agents, one goal — the framework handles the rest:
import { OpenMultiAgent } from '@jackchen_me/open-multi-agent'
import type { AgentConfig } from '@jackchen_me/open-multi-agent'
const architect: AgentConfig = {
name: 'architect',
model: 'claude-sonnet-4-6',
systemPrompt: 'You design clean API contracts and file structures.',
tools: ['file_write'],
}
const developer: AgentConfig = {
name: 'developer',
model: 'claude-sonnet-4-6',
systemPrompt: 'You implement what the architect designs.',
tools: ['bash', 'file_read', 'file_write', 'file_edit'],
}
const reviewer: AgentConfig = {
name: 'reviewer',
model: 'claude-sonnet-4-6',
systemPrompt: 'You review code for correctness and clarity.',
tools: ['file_read', 'grep'],
}
const orchestrator = new OpenMultiAgent({
defaultModel: 'claude-sonnet-4-6',
onProgress: (event) => console.log(event.type, event.agent ?? event.task ?? ''),
})
const team = orchestrator.createTeam('api-team', {
name: 'api-team',
agents: [architect, developer, reviewer],
sharedMemory: true,
})
// Describe a goal — the framework breaks it into tasks and orchestrates execution
const result = await orchestrator.runTeam(team, 'Create a REST API for a todo list in /tmp/todo-api/')
console.log(`Success: ${result.success}`)
console.log(`Tokens: ${result.totalTokenUsage.output_tokens} output tokens`)
What happens under the hood:
agent_start coordinator
task_start architect
task_complete architect
task_start developer
task_start developer // independent tasks run in parallel
task_complete developer
task_start reviewer // unblocked after implementation
task_complete developer
task_complete reviewer
agent_complete coordinator // synthesizes final result
Success: true
Tokens: 12847 output tokens
Three Ways to Run
| Mode | Method | When to use |
|---|---|---|
| Single agent | runAgent() |
One agent, one prompt — simplest entry point |
| Auto-orchestrated team | runTeam() |
Give a goal, framework plans and executes |
| Explicit pipeline | runTasks() |
You define the task graph and assignments |
Contributors
More Examples
Single Agent — one agent, one prompt
import { OpenMultiAgent } from '@jackchen_me/open-multi-agent'
const orchestrator = new OpenMultiAgent({ defaultModel: 'claude-sonnet-4-6' })
const result = await orchestrator.runAgent(
{
name: 'coder',
model: 'claude-sonnet-4-6',
tools: ['bash', 'file_write'],
},
'Write a TypeScript function that reverses a string, save it to /tmp/reverse.ts, and run it.',
)
console.log(result.output)
Task Pipeline — explicit control over task graph and assignments
const result = await orchestrator.runTasks(team, [
{
title: 'Design the data model',
description: 'Write a TypeScript interface spec to /tmp/spec.md',
assignee: 'architect',
},
{
title: 'Implement the module',
description: 'Read /tmp/spec.md and implement the module in /tmp/src/',
assignee: 'developer',
dependsOn: ['Design the data model'], // blocked until design completes
},
{
title: 'Write tests',
description: 'Read the implementation and write Vitest tests.',
assignee: 'developer',
dependsOn: ['Implement the module'],
},
{
title: 'Review code',
description: 'Review /tmp/src/ and produce a structured code review.',
assignee: 'reviewer',
dependsOn: ['Implement the module'], // can run in parallel with tests
},
])
Custom Tools — define tools with Zod schemas
import { z } from 'zod'
import { defineTool, Agent, ToolRegistry, ToolExecutor, registerBuiltInTools } from '@jackchen_me/open-multi-agent'
const searchTool = defineTool({
name: 'web_search',
description: 'Search the web and return the top results.',
inputSchema: z.object({
query: z.string().describe('The search query.'),
maxResults: z.number().optional().describe('Number of results (default 5).'),
}),
execute: async ({ query, maxResults = 5 }) => {
const results = await mySearchProvider(query, maxResults)
return { data: JSON.stringify(results), isError: false }
},
})
const registry = new ToolRegistry()
registerBuiltInTools(registry)
registry.register(searchTool)
const executor = new ToolExecutor(registry)
const agent = new Agent(
{ name: 'researcher', model: 'claude-sonnet-4-6', tools: ['web_search'] },
registry,
executor,
)
const result = await agent.run('Find the three most recent TypeScript releases.')
Multi-Model Teams — mix Claude, GPT, and local models in one workflow
const claudeAgent: AgentConfig = {
name: 'strategist',
model: 'claude-opus-4-6',
provider: 'anthropic',
systemPrompt: 'You plan high-level approaches.',
tools: ['file_write'],
}
const gptAgent: AgentConfig = {
name: 'implementer',
model: 'gpt-5.4',
provider: 'openai',
systemPrompt: 'You implement plans as working code.',
tools: ['bash', 'file_read', 'file_write'],
}
// Any OpenAI-compatible API — Ollama, vLLM, LM Studio, etc.
const localAgent: AgentConfig = {
name: 'reviewer',
model: 'llama3.1',
provider: 'openai',
baseURL: 'http://localhost:11434/v1',
apiKey: 'ollama',
systemPrompt: 'You review code for correctness and clarity.',
tools: ['file_read', 'grep'],
}
const team = orchestrator.createTeam('mixed-team', {
name: 'mixed-team',
agents: [claudeAgent, gptAgent, localAgent],
sharedMemory: true,
})
const result = await orchestrator.runTeam(team, 'Build a CLI tool that converts JSON to CSV.')
Streaming Output
import { Agent, ToolRegistry, ToolExecutor, registerBuiltInTools } from '@jackchen_me/open-multi-agent'
const registry = new ToolRegistry()
registerBuiltInTools(registry)
const executor = new ToolExecutor(registry)
const agent = new Agent(
{ name: 'writer', model: 'claude-sonnet-4-6', maxTurns: 3 },
registry,
executor,
)
for await (const event of agent.stream('Explain monads in two sentences.')) {
if (event.type === 'text' && typeof event.data === 'string') {
process.stdout.write(event.data)
}
}
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ OpenMultiAgent (Orchestrator) │
│ │
│ createTeam() runTeam() runTasks() runAgent() getStatus() │
└──────────────────────┬──────────────────────────────────────────┘
│
┌──────────▼──────────┐
│ Team │
│ - AgentConfig[] │
│ - MessageBus │
│ - TaskQueue │
│ - SharedMemory │
└──────────┬──────────┘
│
┌─────────────┴─────────────┐
│ │
┌────────▼──────────┐ ┌───────────▼───────────┐
│ AgentPool │ │ TaskQueue │
│ - Semaphore │ │ - dependency graph │
│ - runParallel() │ │ - auto unblock │
└────────┬──────────┘ │ - cascade failure │
│ └───────────────────────┘
┌────────▼──────────┐
│ Agent │
│ - run() │ ┌──────────────────────┐
│ - prompt() │───►│ LLMAdapter │
│ - stream() │ │ - AnthropicAdapter │
└────────┬──────────┘ │ - OpenAIAdapter │
│ │ - CopilotAdapter │
│ └──────────────────────┘
┌────────▼──────────┐
│ AgentRunner │ ┌──────────────────────┐
│ - conversation │───►│ ToolRegistry │
│ loop │ │ - defineTool() │
│ - tool dispatch │ │ - 5 built-in tools │
└───────────────────┘ └──────────────────────┘
Built-in Tools
| Tool | Description |
|---|---|
bash |
Execute shell commands. Returns stdout + stderr. Supports timeout and cwd. |
file_read |
Read file contents at an absolute path. Supports offset/limit for large files. |
file_write |
Write or create a file. Auto-creates parent directories. |
file_edit |
Edit a file by replacing an exact string match. |
grep |
Search file contents with regex. Uses ripgrep when available, falls back to Node.js. |
Contributing
Issues, feature requests, and PRs are welcome. Some areas where contributions would be especially valuable:
- LLM Adapters — Anthropic, OpenAI, and Copilot are supported out of the box. Any OpenAI-compatible API (Ollama, vLLM, LM Studio, etc.) works via
baseURL. Additional adapters for Gemini and other providers are welcome. TheLLMAdapterinterface requires just two methods:chat()andstream(). - Examples — Real-world workflows and use cases.
- Documentation — Guides, tutorials, and API docs.
Star History
License
MIT