Open Multi-Agent: Auto-Orchestrated AI Teams in Three Dependencies
Hook
Most multi-agent frameworks add 50+ dependencies and Python runtime baggage. Open Multi-Agent ships the entire orchestration layer—coordinator, DAG scheduler, tool dispatch—in 27 TypeScript files with exactly three npm packages.
Context
Building multi-agent AI systems traditionally meant choosing between two bad options: heavyweight Python frameworks like LangGraph that force you to run separate services and manage subprocess bridges, or DIY solutions where you manually wire task graphs and handle coordination logic yourself. The first approach works for dedicated AI services but breaks down when you need agents embedded in existing TypeScript apps, serverless functions, or edge runtimes. The second approach gives you control but turns every project into framework development.
Open Multi-Agent attacks this gap by treating orchestration as a library, not a service. Instead of deploying agents as separate processes that communicate over HTTP, you import a TypeScript module and call runTeam() with a goal description. A coordinator agent decomposes the goal into tasks, builds a dependency graph, and executes independent work in parallel—all within your Node.js process. The entire framework compiles to under 100KB, runs in Vercel Edge Functions, and has no opinion about your HTTP framework, database, or deployment target. It’s multi-agent orchestration as a first-class TypeScript primitive.
Technical Insight
The architecture centers on a coordinator-worker pattern backed by DAG scheduling. When you invoke runTeam(), the framework spins up a coordinator agent (itself an LLM) that receives your goal and available agent definitions. The coordinator’s system prompt instructs it to output a structured JSON task plan using Zod validation—each task specifies which agent executes it, what inputs it needs, and which other task IDs must complete first. This DAG gets fed into a TaskQueue that tracks dependency resolution and unblocks tasks as their prerequisites finish.
Here’s what a basic team setup looks like:
import { runTeam } from 'open-multi-agent';
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const result = await runTeam({
goal: 'Analyze the top HN story and generate a Twitter thread',
agents: [
{
id: 'scraper',
role: 'Fetches web content and extracts text',
model: anthropic.messages,
tools: [fetchUrlTool, extractTextTool]
},
{
id: 'analyst',
role: 'Summarizes technical content into key insights',
model: anthropic.messages
},
{
id: 'writer',
role: 'Converts insights into engaging Twitter threads',
model: anthropic.messages,
tools: [formatThreadTool]
}
],
onTrace: (span) => console.log(span.type, span.duration, span.tokens)
});
console.log(result.output); // Final thread content
Under the hood, the coordinator might produce a plan like: Task 1 (scraper fetches URL) → Task 2 (analyst summarizes) → Task 3 (writer formats thread). But if you had multiple URLs, it would parallelize: Tasks 1a, 1b, 1c (fetch URLs in parallel) → Task 2 (analyst processes all) → Task 3 (writer outputs thread). The parallel execution happens via an AgentPool with semaphore-based concurrency limits—you specify maxConcurrency and the pool blocks new tasks until slots free up.
The model-agnostic design deserves attention. Each agent wraps an LLM adapter that normalizes OpenAI, Anthropic, and local model interfaces to a common MessageStream protocol. You can mix providers in the same team:
import OpenAI from 'openai';
import Anthropic from '@anthropic-ai/sdk';
const openai = new OpenAI();
const anthropic = new Anthropic();
const ollama = new OpenAI({
baseURL: 'http://localhost:11434/v1',
apiKey: 'ollama'
});
await runTeam({
goal: 'Generate and test API endpoints',
agents: [
{
id: 'architect',
role: 'Designs API structure and schemas',
model: anthropic.messages, // Expensive, smart model for planning
},
{
id: 'coder',
role: 'Writes TypeScript route handlers',
model: ollama.chat.completions, // Free local model for code generation
tools: [writeFileTool]
},
{
id: 'tester',
role: 'Creates integration tests',
model: openai.chat.completions, // GPT-4 for test edge cases
tools: [runTestTool]
}
]
});
Structured output uses Zod schemas to force agents to return validated JSON instead of freeform text. Define a schema, pass it to runAgent(), and the framework automatically adds JSON schema instructions to the system prompt and validates the response:
import { z } from 'zod';
import { runAgent } from 'open-multi-agent';
const BugReport = z.object({
severity: z.enum(['low', 'medium', 'high', 'critical']),
title: z.string(),
steps: z.array(z.string()),
affectedVersions: z.array(z.string())
});
const report = await runAgent({
agent: { id: 'analyst', model: anthropic.messages },
input: 'User reports app crashes when uploading 10MB+ files',
outputSchema: BugReport
});
// TypeScript knows report.output is fully typed:
console.log(report.output.severity); // 'high'
If the LLM returns invalid JSON, the framework automatically retries with the validation error appended to the conversation. This retry logic with exponential backoff is built into the AgentRunner conversation loop—the same component that dispatches tool calls via the ToolRegistry.
Observability hooks into every operation through the onTrace callback, which receives structured spans for LLM calls, tool executions, and task lifecycle events. Each span includes a runId for correlation, parent/child relationships for distributed tracing, and token counts for cost tracking. Because it’s callback-based rather than logging-library-based, there’s zero overhead when you don’t attach a listener—no string formatting, no serialization, no I/O until you explicitly subscribe.
Gotcha
The coordinator agent is a single point of failure and quality bottleneck. If it misunderstands your goal or hallucinates an invalid task plan, the entire execution fails. Unlike frameworks with human-in-the-loop approval, Open Multi-Agent immediately executes whatever DAG the coordinator produces. You can mitigate this by writing more specific goal descriptions and testing with stronger models (Claude Opus vs. Haiku), but there’s no built-in plan visualization or approval step. For high-stakes workflows, you’d need to build a confirmation layer yourself or use runTasks() to manually define the DAG.
Checkpointing and long-running workflow resilience are missing. If your Node.js process crashes mid-execution or a task fails after the retry limit, you lose all progress and start from scratch. There’s no state persistence layer, no intermediate result caching, no resume-from-checkpoint mechanism. Task retry exists with exponential backoff, but it’s scoped to individual tasks—if the coordinator itself fails or your deployment platform kills the container, the entire runTeam() call throws and you re-run everything. This makes it unsuitable for workflows that take hours or process expensive operations where partial progress matters. You’d need to implement your own checkpointing by breaking work into smaller runTeam() calls and persisting results between them.
Verdict
Use if: You’re building TypeScript/Node.js applications (web apps, APIs, CLI tools, serverless functions) that need embedded AI agent orchestration without Python dependencies. Perfect for rapid prototyping of moderate-complexity automation—code generation pipelines, content workflows, data processing tasks—where automatic task decomposition saves you from manually wiring graphs. The three-dependency footprint and readable codebase make it ideal for teams that want to fork, customize, or deeply understand their agent framework. Skip if: You need production-grade resilience like checkpoint/resume for long-running workflows, already have a Python stack where LangGraph or AutoGen offer richer ecosystems, or require advanced agentic features like vector memory, reinforcement learning, or fine-grained cost controls. Also skip if your workflows demand human-in-the-loop approval for AI-generated plans—you’d need to build that layer yourself or choose a framework with built-in collaboration primitives.