Package Exports
- @oni.bot/core
- @oni.bot/core/agents
- @oni.bot/core/checkpointers
- @oni.bot/core/coordination
- @oni.bot/core/functional
- @oni.bot/core/guardrails
- @oni.bot/core/harness
- @oni.bot/core/hitl
- @oni.bot/core/inspect
- @oni.bot/core/messages
- @oni.bot/core/models
- @oni.bot/core/prebuilt
- @oni.bot/core/store
- @oni.bot/core/streaming
- @oni.bot/core/swarm
- @oni.bot/core/testing
- @oni.bot/core/tools
Readme
@oni.bot/core
The graph execution engine for agent swarms.
Production-grade orchestration with zero dependencies.
Why ONI
Most agent frameworks make the easy things easy and the hard things impossible. You can spin up a chatbot in ten lines, but the moment you need four agents coordinating with retries, circuit breakers, and human approval gates, you're writing glue code from scratch.
ONI was built for the hard things:
- Multi-agent orchestration is a first-class primitive. Seven swarm templates ship out of the box -- hierarchical, fan-out, pipeline, peer-network, map-reduce, debate, and hierarchical-mesh. Pick one, plug in your agents, and go.
- Zero runtime dependencies. The entire engine is self-contained TypeScript. No framework lock-in, no transitive supply-chain risk. Runs in Node, serverless functions, and edge runtimes without adaptation.
- Production-grade from day one. Circuit breakers, node timeouts, dead letter queues, retry with exponential backoff, OpenTelemetry tracing, and checkpointing with time-travel debugging. These aren't afterthoughts -- they're wired into the execution engine.
- Type-safe end to end. Full generics on state, channels, and node returns. TypeScript strict mode. If your graph compiles, it runs.
Quick Start
npm install @oni.bot/coreimport { StateGraph, START, END, lastValue, appendList } from "@oni.bot/core";
type State = { query: string; answer: string; log: string[] };
const graph = new StateGraph<State>({
channels: {
query: lastValue(() => ""),
answer: lastValue(() => ""),
log: appendList(() => []),
},
});
graph.addNode("think", async (state) => {
return { answer: `Processed: ${state.query}`, log: ["think ran"] };
});
graph.addEdge(START, "think");
graph.addEdge("think", END);
const app = graph.compile();
const result = await app.invoke({ query: "What is ONI?" });
console.log(result.answer); // "Processed: What is ONI?"Multi-Agent Swarm
Build coordinated agent teams with a single function call:
import { SwarmGraph } from "@oni.bot/core/swarm";
import { defineAgent } from "@oni.bot/core/agents";
import { anthropic } from "@oni.bot/core/models";
const researcher = defineAgent({
name: "researcher",
model: anthropic("claude-sonnet-4-6"),
systemPrompt: "You research topics thoroughly.",
tools: [webSearch],
});
const writer = defineAgent({
name: "writer",
model: anthropic("claude-sonnet-4-6"),
systemPrompt: "You write compelling content from research notes.",
});
const swarm = SwarmGraph.hierarchical({
supervisor: { model: anthropic("claude-sonnet-4-6"), strategy: "llm", maxRounds: 10 },
agents: [researcher, writer],
});
const app = swarm.compile();
const result = await app.invoke({ task: "Write a technical brief on quantum error correction" });No boilerplate routing, no manual state plumbing. The supervisor decides which agent runs next, results flow back through typed channels, and the swarm terminates when the task is complete.
Features
| Category | Feature | Description |
|---|---|---|
| Engine | Pregel execution | Superstep-parallel graph engine with deterministic state merging |
| Typed channels | lastValue, appendList, mergeObject, ephemeralValue reducers |
|
| Command routing | State update + routing in a single return value | |
| Send API | Dynamic fan-out to nodes with per-instance payloads | |
| Subgraphs | Nested compiled graphs with Command.PARENT state bridging |
|
| Swarm | 7 templates | Hierarchical, fan-out, pipeline, peer-network, map-reduce, debate, mesh |
| Supervisor | LLM-based or rule-based routing strategies | |
| Handoff | Agent-to-agent delegation with context transfer | |
| Coordination | RequestReplyBroker, PubSub for inter-agent messaging |
|
| Models | 5 LLM adapters | Anthropic, OpenAI, Google, Ollama, OpenRouter |
| Unified interface | ONIModel with chat, streaming, and tool calling |
|
| Mock model | Deterministic test doubles with call history | |
| Streaming | 5 modes | values, updates, debug, messages, custom |
| Token streaming | Character-level LLM output via messages mode |
|
| Custom events | Emit named events from any node via StreamWriter |
|
| Multi-mode | Subscribe to multiple stream modes simultaneously | |
| Reliability | Circuit breakers | Automatic failure detection with configurable thresholds |
| Node timeouts | Per-node deadline enforcement | |
| Dead letter queues | Capture and inspect failed executions | |
| Retry + backoff | Configurable per-node retry with exponential backoff | |
| Persistence | Checkpointing | Memory, SQLite, PostgreSQL backends |
| Time travel | getHistory, getStateAt, forkFrom |
|
| Cross-thread store | Namespaced KV with semantic search for agent memory | |
| HITL | Interrupts | interrupt(), getUserInput(), getUserApproval() |
| Session management | Typed resume values, multi-step approval flows | |
| Guardrails | Budget tracking | Token and cost limits with automatic enforcement |
| Content filters | PII detection, topic filtering, custom validators | |
| Permissions | Per-tool permission checks with audit logging | |
| Audit log | Structured logging of all guardrail decisions | |
| Observability | OpenTelemetry | Zero-dep adapter -- bring your own tracer |
| Graph inspection | Topology descriptors, Mermaid diagrams, cycle detection | |
| Testing | mockModel() |
Deterministic model stubs with call history tracking |
assertGraph() |
Structural assertions on graph topology | |
createTestHarness() |
Pre-wired test runner with auto-checkpointing | |
| API Styles | Builder pattern | StateGraph + addNode + addEdge + compile |
| Functional | task(), entrypoint(), pipe(), branch() |
|
| Prebuilt | createReactAgent(), defineAgent() |
Swarm Templates
| Template | Pattern | When to use it |
|---|---|---|
SwarmGraph.hierarchical() |
Supervisor routes to workers | General multi-agent tasks with centralized control |
SwarmGraph.fanOut() |
Parallel execution + aggregation | Independent subtasks that can run concurrently |
SwarmGraph.pipeline() |
Sequential A -> B -> C chain | Ordered processing stages (ETL, content pipelines) |
SwarmGraph.peerNetwork() |
Dynamic peer-to-peer handoff | Agents self-organize based on capabilities |
SwarmGraph.mapReduce() |
Split -> Pool -> Reduce | Distributing N items across a worker pool |
SwarmGraph.debate() |
Judge -> Debaters -> Consensus | Multi-perspective reasoning, red-teaming |
SwarmGraph.hierarchicalMesh() |
Coordinator -> Team subgraphs | Nested teams with inter-team coordination |
Each template handles routing, error recovery, and termination automatically. See examples/swarm/ for runnable examples.
Architecture
@oni.bot/core v0.7.0
+--------------------------+
| Your Application |
+--------------------------+
|
+--------------------+--------------------+
| | |
defineAgent() StateGraph SwarmGraph
(single agent) (custom graph) (7 templates)
| | |
+--------------------+--------------------+
|
+-----------+-----------+
| ONIPregelRunner |
| (execution engine) |
+-----------+-----------+
|
+----------+----------+-------+-------+----------+----------+
| | | | | |
Channels Streaming Checkpoint Circuit Runtime Telemetry
(reducers) (5 modes) (3 backends) Breaker Context (OTel)
+ Retry (AsyncLocal)
+ DLQEntry points at every level of abstraction:
ONIModel-- Call an LLM directlydefineAgent()-- Single agent with tools and system promptStateGraph-- Custom graph with full control over nodes and edgesSwarmGraph-- Multi-agent orchestration from templates
Sub-module Imports
Tree-shakeable sub-module imports for bundle optimization:
import { StateGraph, START, END } from "@oni.bot/core"; // Core engine
import { createReactAgent } from "@oni.bot/core/prebuilt"; // Prebuilt agents
import { SwarmGraph } from "@oni.bot/core/swarm"; // Swarm templates
import { interrupt } from "@oni.bot/core/hitl"; // Human-in-the-loop
import { InMemoryStore } from "@oni.bot/core/store"; // Cross-thread store
import { messagesChannel } from "@oni.bot/core/messages"; // Message handling
import { SqliteCheckpointer } from "@oni.bot/core/checkpointers";// Persistence
import { entrypoint, task } from "@oni.bot/core/functional"; // Functional API
import { buildGraphDescriptor } from "@oni.bot/core/inspect"; // Graph inspection
import { emitToken } from "@oni.bot/core/streaming"; // Token streaming
import { anthropic, openai } from "@oni.bot/core/models"; // LLM adapters
import { defineAgent } from "@oni.bot/core/agents"; // Agent builder
import { RequestReplyBroker } from "@oni.bot/core/coordination"; // Agent messaging
import { BudgetTracker } from "@oni.bot/core/guardrails"; // Safety controls
import { defineTool } from "@oni.bot/core/tools"; // Tool definitions
import { mockModel } from "@oni.bot/core/testing"; // Test utilities16 entry points. Import only what you use.
Production Features
Circuit Breakers
const app = graph.compile({
circuitBreaker: { threshold: 5, resetAfter: 30_000 },
});After 5 consecutive failures, the circuit opens and fast-fails all requests. After 30 seconds, it transitions to half-open and lets one request through to test recovery.
Node Timeouts
graph.addNode("llm_call", handler, { timeout: 10_000 }); // 10s deadlineNodes that exceed their timeout are killed and routed through the error recovery path.
Retry with Backoff
graph.addNode("flaky_api", handler, {
retry: { maxAttempts: 3, backoff: "exponential", initialDelay: 1000 },
});Dead Letter Queue
const dlq = new DeadLetterQueue();
const app = graph.compile({ dlq });
// Later: inspect failures
const failures = dlq.list();OpenTelemetry Tracing
import { ONITracer } from "@oni.bot/core";
import { trace } from "@opentelemetry/api";
const tracer = new ONITracer(trace.getTracer("my-app"));
const app = graph.compile({ tracer });Zero-dep adapter that wraps any OpenTelemetry-compatible tracer. Emits spans for graph execution, node runs, tool calls, model invocations, and checkpoint operations.
Checkpointing + Time Travel
import { SqliteCheckpointer } from "@oni.bot/core/checkpointers";
const checkpointer = new SqliteCheckpointer("./state.db");
const app = graph.compile({ checkpointer });
// Resume from any point
const history = await app.getHistory("thread-1");
const forked = await app.forkFrom("thread-1", history[2].checkpoint);Three built-in backends: MemoryCheckpointer (dev), SqliteCheckpointer (single-node), PostgresCheckpointer (distributed). Or implement ONICheckpointer for your own.
Human-in-the-Loop
import { interrupt, getUserApproval } from "@oni.bot/core/hitl";
graph.addNode("review", async (state) => {
const approved = await getUserApproval("Publish this draft?", {
payload: state.draft,
});
if (!approved) return { status: "rejected" };
return { status: "published" };
});Execution pauses, the interrupt is surfaced to your application, and the graph resumes exactly where it left off when the user responds.
Streaming
// Single mode
for await (const event of app.stream(input, { streamMode: "updates" })) {
console.log(event.node, event.data);
}
// Multiple modes simultaneously
for await (const event of app.stream(input, { streamMode: ["values", "messages"] })) {
if (event.mode === "messages") {
process.stdout.write(event.data.chunk); // Token-by-token LLM output
}
}
// Custom events from inside a node
import { getStreamWriter } from "@oni.bot/core";
graph.addNode("agent", async (state) => {
const writer = getStreamWriter();
writer.emit("progress", { step: 1, message: "Searching..." });
// ...
});Functional API
For simpler use cases, skip the builder pattern entirely:
import { entrypoint, task } from "@oni.bot/core/functional";
import { lastValue } from "@oni.bot/core";
const summarize = task("summarize", async (text: string) => {
return await llm.chat({ messages: [{ role: "user", content: `Summarize: ${text}` }] });
});
const app = entrypoint(
{ channels: { query: lastValue(() => ""), answer: lastValue(() => "") } },
async (state) => ({ answer: await summarize(state.query) }),
);
const result = await app.invoke({ query: "Explain quantum computing" });Testing
import { mockModel, assertGraph, createTestHarness } from "@oni.bot/core/testing";
// Deterministic model responses
const model = mockModel([
{ role: "assistant", content: "Hello!" },
{ role: "assistant", content: "Goodbye!", toolCalls: [{ id: "1", name: "search", args: {} }] },
]);
// Structural graph assertions
assertGraph(graph, {
hasNode: ["agent", "tools"],
hasEdge: [["__start__", "agent"]],
nodeCount: 2,
});
// Pre-wired test harness
const harness = createTestHarness(graph);
const result = await harness.invoke({ query: "test" });
const events = await harness.collectStream({ query: "test" }, "updates");Documentation
| Resource | Description |
|---|---|
| Developer Guide | Progressive tutorial from zero to advanced -- 19 sections covering every feature |
| API Reference | Complete reference for all 130+ public exports |
| Examples | 30+ runnable example files covering every feature |
Optional Peer Dependencies
The core engine has zero runtime dependencies. Optional packages unlock additional backends:
npm install better-sqlite3 # SqliteCheckpointer
npm install pg # PostgresCheckpointerContributing
Contributions are welcome. Please open an issue to discuss significant changes before submitting a PR.
git clone https://github.com/oni-bot/core.git
cd core
npm install
npm testLicense
MIT -- ONI Platform