@oni.bot/core 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +111 -0
- package/README.md +315 -200
- package/SECURITY.md +71 -0
- package/dist/checkpointers/sqlite.d.ts.map +1 -1
- package/dist/checkpointers/sqlite.js +20 -18
- package/dist/checkpointers/sqlite.js.map +1 -1
- package/dist/circuit-breaker.d.ts +20 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +58 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +32 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +17 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/templates.d.ts +8 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +119 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/dlq.d.ts +17 -0
- package/dist/dlq.d.ts.map +1 -0
- package/dist/dlq.js +41 -0
- package/dist/dlq.js.map +1 -0
- package/dist/errors.d.ts +36 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +163 -7
- package/dist/errors.js.map +1 -1
- package/dist/graph.d.ts +17 -0
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +13 -2
- package/dist/graph.js.map +1 -1
- package/dist/harness/agent-loop.d.ts +8 -0
- package/dist/harness/agent-loop.d.ts.map +1 -0
- package/dist/harness/agent-loop.js +327 -0
- package/dist/harness/agent-loop.js.map +1 -0
- package/dist/harness/context-compactor.d.ts +73 -0
- package/dist/harness/context-compactor.d.ts.map +1 -0
- package/dist/harness/context-compactor.js +162 -0
- package/dist/harness/context-compactor.js.map +1 -0
- package/dist/harness/harness.d.ts +41 -0
- package/dist/harness/harness.d.ts.map +1 -0
- package/dist/harness/harness.js +140 -0
- package/dist/harness/harness.js.map +1 -0
- package/dist/harness/hooks-engine.d.ts +71 -0
- package/dist/harness/hooks-engine.d.ts.map +1 -0
- package/dist/harness/hooks-engine.js +232 -0
- package/dist/harness/hooks-engine.js.map +1 -0
- package/dist/harness/index.d.ts +16 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +19 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/safety-gate.d.ts +29 -0
- package/dist/harness/safety-gate.d.ts.map +1 -0
- package/dist/harness/safety-gate.js +72 -0
- package/dist/harness/safety-gate.js.map +1 -0
- package/dist/harness/skill-loader.d.ts +63 -0
- package/dist/harness/skill-loader.d.ts.map +1 -0
- package/dist/harness/skill-loader.js +214 -0
- package/dist/harness/skill-loader.js.map +1 -0
- package/dist/harness/todo-module.d.ts +39 -0
- package/dist/harness/todo-module.d.ts.map +1 -0
- package/dist/harness/todo-module.js +179 -0
- package/dist/harness/todo-module.js.map +1 -0
- package/dist/harness/types.d.ts +78 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +9 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/hitl/interrupt.d.ts.map +1 -1
- package/dist/hitl/interrupt.js +7 -6
- package/dist/hitl/interrupt.js.map +1 -1
- package/dist/index.d.ts +14 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/models/google.d.ts.map +1 -1
- package/dist/models/google.js +7 -6
- package/dist/models/google.js.map +1 -1
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -0
- package/dist/models/index.js.map +1 -1
- package/dist/models/openai.js +6 -1
- package/dist/models/openai.js.map +1 -1
- package/dist/models/openrouter.d.ts +13 -0
- package/dist/models/openrouter.d.ts.map +1 -0
- package/dist/models/openrouter.js +322 -0
- package/dist/models/openrouter.js.map +1 -0
- package/dist/prebuilt/tool-node.d.ts.map +1 -1
- package/dist/prebuilt/tool-node.js +0 -1
- package/dist/prebuilt/tool-node.js.map +1 -1
- package/dist/pregel.d.ts +11 -1
- package/dist/pregel.d.ts.map +1 -1
- package/dist/pregel.js +88 -7
- package/dist/pregel.js.map +1 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +6 -1
- package/dist/retry.js.map +1 -1
- package/dist/store/index.d.ts +10 -0
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +15 -1
- package/dist/store/index.js.map +1 -1
- package/dist/stream-events.js +2 -2
- package/dist/stream-events.js.map +1 -1
- package/dist/streaming.d.ts +10 -0
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +28 -0
- package/dist/streaming.js.map +1 -1
- package/dist/swarm/graph.d.ts.map +1 -1
- package/dist/swarm/graph.js +0 -4
- package/dist/swarm/graph.js.map +1 -1
- package/dist/swarm/supervisor.d.ts.map +1 -1
- package/dist/swarm/supervisor.js +0 -4
- package/dist/swarm/supervisor.js.map +1 -1
- package/dist/telemetry.d.ts +41 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +69 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/testing/index.d.ts +33 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +95 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +38 -5
package/README.md
CHANGED
|
@@ -1,57 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<strong>@oni.bot/core</strong>
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
>
|
|
4
|
-
> Zero dependencies. Full TypeScript generics. Pregel-based superstep engine. 7 swarm templates.
|
|
5
|
+
<h3 align="center">The graph execution engine for agent swarms.</h3>
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
Production-grade orchestration with zero dependencies.
|
|
9
|
+
</p>
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **Graph execution** — Pregel superstep model with parallel node execution, fan-out/fan-in, map-reduce
|
|
16
|
-
- **5 stream modes** — `values`, `updates`, `debug`, `messages` (token-level), and `custom` events
|
|
17
|
-
- **Checkpointing** — Memory, SQLite, PostgreSQL, or custom backends with time travel and fork
|
|
18
|
-
- **Human-in-the-loop** — compile-time interrupts + runtime `interrupt()` with resume, typed input, approval, selection
|
|
19
|
-
- **Messages** — smart reducer with deduplication, `RemoveMessage`, `UpdateMessage`, filtering, trimming
|
|
20
|
-
- **Cross-thread Store** — namespaced KV store with semantic search for agent long-term memory
|
|
21
|
-
- **Runtime context** — `getConfig()`, `getStore()`, `getStreamWriter()` via AsyncLocalStorage
|
|
22
|
-
- **Swarm orchestration** — 7 swarm templates: hierarchical, fan-out, pipeline, peer-network, map-reduce, debate, hierarchical-mesh
|
|
23
|
-
- **Functional API** — `task()`, `entrypoint()`, `pipe()`, `branch()` as alternatives to the builder pattern
|
|
24
|
-
- **Prebuilt agents** — `createReactAgent()` with bring-your-own LLM
|
|
25
|
-
- **Injected tools** — tools that auto-receive state + store from runtime context
|
|
26
|
-
- **Graph inspection** — topology descriptors, Mermaid diagrams, cycle detection
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@oni.bot/core"><img src="https://img.shields.io/npm/v/@oni.bot/core.svg" alt="npm version" /></a>
|
|
13
|
+
<a href="https://github.com/oni-bot/core/actions"><img src="https://img.shields.io/github/actions/workflow/status/oni-bot/core/ci.yml?label=tests" alt="tests" /></a>
|
|
14
|
+
<img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="zero dependencies" />
|
|
15
|
+
<img src="https://img.shields.io/badge/TypeScript-strict-blue" alt="TypeScript strict" />
|
|
16
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License" /></a>
|
|
17
|
+
</p>
|
|
27
18
|
|
|
28
19
|
---
|
|
29
20
|
|
|
30
|
-
##
|
|
21
|
+
## Why ONI
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
npm install @oni.bot/core
|
|
34
|
-
```
|
|
23
|
+
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.
|
|
35
24
|
|
|
36
|
-
|
|
25
|
+
ONI was built for the hard things:
|
|
37
26
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
- **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.
|
|
28
|
+
- **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.
|
|
29
|
+
- **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.
|
|
30
|
+
- **Type-safe end to end.** Full generics on state, channels, and node returns. TypeScript strict mode. If your graph compiles, it runs.
|
|
42
31
|
|
|
43
32
|
---
|
|
44
33
|
|
|
45
34
|
## Quick Start
|
|
46
35
|
|
|
47
|
-
|
|
36
|
+
```bash
|
|
37
|
+
npm install @oni.bot/core
|
|
38
|
+
```
|
|
48
39
|
|
|
49
40
|
```ts
|
|
50
41
|
import { StateGraph, START, END, lastValue, appendList } from "@oni.bot/core";
|
|
51
42
|
|
|
52
|
-
type
|
|
43
|
+
type State = { query: string; answer: string; log: string[] };
|
|
53
44
|
|
|
54
|
-
const graph = new StateGraph<
|
|
45
|
+
const graph = new StateGraph<State>({
|
|
55
46
|
channels: {
|
|
56
47
|
query: lastValue(() => ""),
|
|
57
48
|
answer: lastValue(() => ""),
|
|
@@ -59,250 +50,374 @@ const graph = new StateGraph<MyState>({
|
|
|
59
50
|
},
|
|
60
51
|
});
|
|
61
52
|
|
|
62
|
-
graph.addNode("
|
|
63
|
-
return { answer: `
|
|
53
|
+
graph.addNode("think", async (state) => {
|
|
54
|
+
return { answer: `Processed: ${state.query}`, log: ["think ran"] };
|
|
64
55
|
});
|
|
65
56
|
|
|
66
|
-
graph.addEdge(START, "
|
|
67
|
-
graph.addEdge("
|
|
57
|
+
graph.addEdge(START, "think");
|
|
58
|
+
graph.addEdge("think", END);
|
|
68
59
|
|
|
69
60
|
const app = graph.compile();
|
|
70
61
|
const result = await app.invoke({ query: "What is ONI?" });
|
|
62
|
+
console.log(result.answer); // "Processed: What is ONI?"
|
|
71
63
|
```
|
|
72
64
|
|
|
73
|
-
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Multi-Agent Swarm
|
|
68
|
+
|
|
69
|
+
Build coordinated agent teams with a single function call:
|
|
74
70
|
|
|
75
71
|
```ts
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
72
|
+
import { SwarmGraph } from "@oni.bot/core/swarm";
|
|
73
|
+
import { defineAgent } from "@oni.bot/core/agents";
|
|
74
|
+
import { anthropic } from "@oni.bot/core/models";
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
const researcher = defineAgent({
|
|
77
|
+
name: "researcher",
|
|
78
|
+
model: anthropic("claude-sonnet-4-6"),
|
|
79
|
+
systemPrompt: "You research topics thoroughly.",
|
|
80
|
+
tools: [webSearch],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const writer = defineAgent({
|
|
84
|
+
name: "writer",
|
|
85
|
+
model: anthropic("claude-sonnet-4-6"),
|
|
86
|
+
systemPrompt: "You write compelling content from research notes.",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const swarm = SwarmGraph.hierarchical({
|
|
90
|
+
supervisor: { model: anthropic("claude-sonnet-4-6"), strategy: "llm", maxRounds: 10 },
|
|
91
|
+
agents: [researcher, writer],
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const app = swarm.compile();
|
|
95
|
+
const result = await app.invoke({ task: "Write a technical brief on quantum error correction" });
|
|
84
96
|
```
|
|
85
97
|
|
|
86
|
-
|
|
98
|
+
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.
|
|
87
99
|
|
|
88
|
-
|
|
89
|
-
import { entrypoint, lastValue } from "@oni.bot/core";
|
|
100
|
+
---
|
|
90
101
|
|
|
91
|
-
|
|
92
|
-
{ channels: { query: lastValue(() => ""), answer: lastValue(() => "") } },
|
|
93
|
-
async (state) => ({ answer: await myLLM(state.query) })
|
|
94
|
-
);
|
|
102
|
+
## Features
|
|
95
103
|
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
| Category | Feature | Description |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| **Engine** | Pregel execution | Superstep-parallel graph engine with deterministic state merging |
|
|
107
|
+
| | Typed channels | `lastValue`, `appendList`, `mergeObject`, `ephemeralValue` reducers |
|
|
108
|
+
| | Command routing | State update + routing in a single return value |
|
|
109
|
+
| | Send API | Dynamic fan-out to nodes with per-instance payloads |
|
|
110
|
+
| | Subgraphs | Nested compiled graphs with `Command.PARENT` state bridging |
|
|
111
|
+
| **Swarm** | 7 templates | Hierarchical, fan-out, pipeline, peer-network, map-reduce, debate, mesh |
|
|
112
|
+
| | Supervisor | LLM-based or rule-based routing strategies |
|
|
113
|
+
| | Handoff | Agent-to-agent delegation with context transfer |
|
|
114
|
+
| | Coordination | `RequestReplyBroker`, `PubSub` for inter-agent messaging |
|
|
115
|
+
| **Models** | 5 LLM adapters | Anthropic, OpenAI, Google, Ollama, OpenRouter |
|
|
116
|
+
| | Unified interface | `ONIModel` with chat, streaming, and tool calling |
|
|
117
|
+
| | Mock model | Deterministic test doubles with call history |
|
|
118
|
+
| **Streaming** | 5 modes | `values`, `updates`, `debug`, `messages`, `custom` |
|
|
119
|
+
| | Token streaming | Character-level LLM output via `messages` mode |
|
|
120
|
+
| | Custom events | Emit named events from any node via `StreamWriter` |
|
|
121
|
+
| | Multi-mode | Subscribe to multiple stream modes simultaneously |
|
|
122
|
+
| **Reliability** | Circuit breakers | Automatic failure detection with configurable thresholds |
|
|
123
|
+
| | Node timeouts | Per-node deadline enforcement |
|
|
124
|
+
| | Dead letter queues | Capture and inspect failed executions |
|
|
125
|
+
| | Retry + backoff | Configurable per-node retry with exponential backoff |
|
|
126
|
+
| **Persistence** | Checkpointing | Memory, SQLite, PostgreSQL backends |
|
|
127
|
+
| | Time travel | `getHistory`, `getStateAt`, `forkFrom` |
|
|
128
|
+
| | Cross-thread store | Namespaced KV with semantic search for agent memory |
|
|
129
|
+
| **HITL** | Interrupts | `interrupt()`, `getUserInput()`, `getUserApproval()` |
|
|
130
|
+
| | Session management | Typed resume values, multi-step approval flows |
|
|
131
|
+
| **Guardrails** | Budget tracking | Token and cost limits with automatic enforcement |
|
|
132
|
+
| | Content filters | PII detection, topic filtering, custom validators |
|
|
133
|
+
| | Permissions | Per-tool permission checks with audit logging |
|
|
134
|
+
| | Audit log | Structured logging of all guardrail decisions |
|
|
135
|
+
| **Observability** | OpenTelemetry | Zero-dep adapter -- bring your own tracer |
|
|
136
|
+
| | Graph inspection | Topology descriptors, Mermaid diagrams, cycle detection |
|
|
137
|
+
| **Testing** | `mockModel()` | Deterministic model stubs with call history tracking |
|
|
138
|
+
| | `assertGraph()` | Structural assertions on graph topology |
|
|
139
|
+
| | `createTestHarness()` | Pre-wired test runner with auto-checkpointing |
|
|
140
|
+
| **API Styles** | Builder pattern | `StateGraph` + `addNode` + `addEdge` + `compile` |
|
|
141
|
+
| | Functional | `task()`, `entrypoint()`, `pipe()`, `branch()` |
|
|
142
|
+
| | Prebuilt | `createReactAgent()`, `defineAgent()` |
|
|
98
143
|
|
|
99
144
|
---
|
|
100
145
|
|
|
101
146
|
## Swarm Templates
|
|
102
147
|
|
|
103
|
-
|
|
148
|
+
| Template | Pattern | When to use it |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `SwarmGraph.hierarchical()` | Supervisor routes to workers | General multi-agent tasks with centralized control |
|
|
151
|
+
| `SwarmGraph.fanOut()` | Parallel execution + aggregation | Independent subtasks that can run concurrently |
|
|
152
|
+
| `SwarmGraph.pipeline()` | Sequential A -> B -> C chain | Ordered processing stages (ETL, content pipelines) |
|
|
153
|
+
| `SwarmGraph.peerNetwork()` | Dynamic peer-to-peer handoff | Agents self-organize based on capabilities |
|
|
154
|
+
| `SwarmGraph.mapReduce()` | Split -> Pool -> Reduce | Distributing N items across a worker pool |
|
|
155
|
+
| `SwarmGraph.debate()` | Judge -> Debaters -> Consensus | Multi-perspective reasoning, red-teaming |
|
|
156
|
+
| `SwarmGraph.hierarchicalMesh()` | Coordinator -> Team subgraphs | Nested teams with inter-team coordination |
|
|
104
157
|
|
|
105
|
-
|
|
158
|
+
Each template handles routing, error recovery, and termination automatically. See `examples/swarm/` for runnable examples.
|
|
106
159
|
|
|
107
|
-
|
|
108
|
-
import { SwarmGraph } from "@oni.bot/core/swarm";
|
|
160
|
+
---
|
|
109
161
|
|
|
110
|
-
|
|
111
|
-
supervisor: { model: myModel, strategy: "llm", maxRounds: 10 },
|
|
112
|
-
agents: [researcher, writer, critic],
|
|
113
|
-
onError: "fallback",
|
|
114
|
-
});
|
|
162
|
+
## Architecture
|
|
115
163
|
|
|
116
|
-
|
|
117
|
-
|
|
164
|
+
```
|
|
165
|
+
@oni.bot/core v0.7.0
|
|
166
|
+
|
|
167
|
+
+--------------------------+
|
|
168
|
+
| Your Application |
|
|
169
|
+
+--------------------------+
|
|
170
|
+
|
|
|
171
|
+
+--------------------+--------------------+
|
|
172
|
+
| | |
|
|
173
|
+
defineAgent() StateGraph SwarmGraph
|
|
174
|
+
(single agent) (custom graph) (7 templates)
|
|
175
|
+
| | |
|
|
176
|
+
+--------------------+--------------------+
|
|
177
|
+
|
|
|
178
|
+
+-----------+-----------+
|
|
179
|
+
| ONIPregelRunner |
|
|
180
|
+
| (execution engine) |
|
|
181
|
+
+-----------+-----------+
|
|
182
|
+
|
|
|
183
|
+
+----------+----------+-------+-------+----------+----------+
|
|
184
|
+
| | | | | |
|
|
185
|
+
Channels Streaming Checkpoint Circuit Runtime Telemetry
|
|
186
|
+
(reducers) (5 modes) (3 backends) Breaker Context (OTel)
|
|
187
|
+
+ Retry (AsyncLocal)
|
|
188
|
+
+ DLQ
|
|
118
189
|
```
|
|
119
190
|
|
|
120
|
-
|
|
191
|
+
Entry points at every level of abstraction:
|
|
121
192
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
| `SwarmGraph.pipeline()` | Sequential processing chain | A -> B -> C -> END |
|
|
127
|
-
| `SwarmGraph.peerNetwork()` | Agents hand off to each other | Dynamic peer-to-peer routing |
|
|
128
|
-
| `SwarmGraph.mapReduce()` | Distribute N items across pool | Split -> AgentPool -> Collect -> Reduce |
|
|
129
|
-
| `SwarmGraph.debate()` | Multi-round argumentation | Judge -> Debaters -> Judge -> consensus? |
|
|
130
|
-
| `SwarmGraph.hierarchicalMesh()` | Nested supervisor teams | Coordinator -> Team subgraphs -> Coordinator |
|
|
193
|
+
- **`ONIModel`** -- Call an LLM directly
|
|
194
|
+
- **`defineAgent()`** -- Single agent with tools and system prompt
|
|
195
|
+
- **`StateGraph`** -- Custom graph with full control over nodes and edges
|
|
196
|
+
- **`SwarmGraph`** -- Multi-agent orchestration from templates
|
|
131
197
|
|
|
132
|
-
|
|
198
|
+
---
|
|
133
199
|
|
|
134
|
-
|
|
200
|
+
## Sub-module Imports
|
|
135
201
|
|
|
136
|
-
|
|
202
|
+
Tree-shakeable sub-module imports for bundle optimization:
|
|
137
203
|
|
|
138
204
|
```ts
|
|
139
|
-
import {
|
|
140
|
-
import {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
205
|
+
import { StateGraph, START, END } from "@oni.bot/core"; // Core engine
|
|
206
|
+
import { createReactAgent } from "@oni.bot/core/prebuilt"; // Prebuilt agents
|
|
207
|
+
import { SwarmGraph } from "@oni.bot/core/swarm"; // Swarm templates
|
|
208
|
+
import { interrupt } from "@oni.bot/core/hitl"; // Human-in-the-loop
|
|
209
|
+
import { InMemoryStore } from "@oni.bot/core/store"; // Cross-thread store
|
|
210
|
+
import { messagesChannel } from "@oni.bot/core/messages"; // Message handling
|
|
211
|
+
import { SqliteCheckpointer } from "@oni.bot/core/checkpointers";// Persistence
|
|
212
|
+
import { entrypoint, task } from "@oni.bot/core/functional"; // Functional API
|
|
213
|
+
import { buildGraphDescriptor } from "@oni.bot/core/inspect"; // Graph inspection
|
|
214
|
+
import { emitToken } from "@oni.bot/core/streaming"; // Token streaming
|
|
215
|
+
import { anthropic, openai } from "@oni.bot/core/models"; // LLM adapters
|
|
216
|
+
import { defineAgent } from "@oni.bot/core/agents"; // Agent builder
|
|
217
|
+
import { RequestReplyBroker } from "@oni.bot/core/coordination"; // Agent messaging
|
|
218
|
+
import { BudgetTracker } from "@oni.bot/core/guardrails"; // Safety controls
|
|
219
|
+
import { defineTool } from "@oni.bot/core/tools"; // Tool definitions
|
|
220
|
+
import { mockModel } from "@oni.bot/core/testing"; // Test utilities
|
|
150
221
|
```
|
|
151
222
|
|
|
152
|
-
|
|
223
|
+
16 entry points. Import only what you use.
|
|
153
224
|
|
|
154
225
|
---
|
|
155
226
|
|
|
156
|
-
## Features
|
|
157
|
-
|
|
158
|
-
| Feature | Description | Example | Guide |
|
|
159
|
-
|---|---|---|---|
|
|
160
|
-
| Channels | Typed state with pluggable reducers | `examples/ephemeral-channels.ts` | [Section 2](docs/GUIDE.md#2-channels-deep-dive) |
|
|
161
|
-
| Command routing | State update + routing in one return | `examples/command-routing.ts` | [Section 3](docs/GUIDE.md#3-edges-and-routing) |
|
|
162
|
-
| Token streaming | `messages` mode + `getStreamWriter()` | `examples/messages-stream.ts` | [Section 4](docs/GUIDE.md#4-streaming) |
|
|
163
|
-
| Multi-stream | Multiple stream modes simultaneously | `examples/multi-stream.ts` | [Section 4](docs/GUIDE.md#4-streaming) |
|
|
164
|
-
| Custom events | Emit named events via StreamWriter | `examples/custom-stream.ts` | [Section 4](docs/GUIDE.md#4-streaming) |
|
|
165
|
-
| Messages | Smart reducer, helpers, RemoveMessage | `examples/messages-reducer.ts` | [Section 5](docs/GUIDE.md#5-messages) |
|
|
166
|
-
| Checkpointing | Memory, SQLite, PostgreSQL backends | `examples/time-travel.ts` | [Section 6](docs/GUIDE.md#6-checkpointing) |
|
|
167
|
-
| HITL interrupts | `interrupt()`, `getUserApproval()` | `examples/hitl/` | [Section 7](docs/GUIDE.md#7-human-in-the-loop) |
|
|
168
|
-
| Dynamic interrupts | Runtime breakpoint conditions | `examples/dynamic-interrupt.ts` | [Section 7](docs/GUIDE.md#7-human-in-the-loop) |
|
|
169
|
-
| Cross-thread Store | Namespaced KV with semantic search | `examples/store-memory.ts` | [Section 8](docs/GUIDE.md#8-cross-thread-store) |
|
|
170
|
-
| Subgraphs | Nested skeletons + Command.PARENT | `examples/subgraph.ts` | [Section 9](docs/GUIDE.md#9-subgraphs) |
|
|
171
|
-
| Parallel fan-out | Static + dynamic (Send API) | `examples/parallel-fanout.ts` | [Section 10](docs/GUIDE.md#10-parallel-execution) |
|
|
172
|
-
| Map-reduce | Send + fan-in barrier | `examples/map-reduce.ts` | [Section 10](docs/GUIDE.md#10-parallel-execution) |
|
|
173
|
-
| Runtime context | `getConfig()`, `getStore()`, etc. | `examples/runtime-context.ts` | [Section 11](docs/GUIDE.md#11-runtime-context) |
|
|
174
|
-
| Retry + cache | Per-node retry policy with backoff | `examples/retry-policy.ts` | [Section 12](docs/GUIDE.md#12-retry-and-cache) |
|
|
175
|
-
| Functional API | `task`, `entrypoint`, `pipe`, `branch` | `examples/functional-api.ts` | [Section 13](docs/GUIDE.md#13-functional-api) |
|
|
176
|
-
| ReAct agent | Prebuilt agent loop with tools | `examples/react-agent.ts` | [Section 14](docs/GUIDE.md#14-prebuilt-components) |
|
|
177
|
-
| Graph inspection | Topology, Mermaid, cycle detection | `examples/graph-inspection.ts` | [Section 16](docs/GUIDE.md#16-graph-inspection) |
|
|
178
|
-
| Time travel | `getHistory`, `getStateAt`, `forkFrom` | `examples/time-travel.ts` | [Section 17](docs/GUIDE.md#17-time-travel) |
|
|
179
|
-
| Swarm | 7 templates + Supervisor, Handoff, retry-fallback, coordination | `examples/swarm/` | [Section 18](docs/GUIDE.md#18-swarm-orchestration) |
|
|
227
|
+
## Production Features
|
|
180
228
|
|
|
181
|
-
|
|
229
|
+
### Circuit Breakers
|
|
182
230
|
|
|
183
|
-
|
|
231
|
+
```ts
|
|
232
|
+
const app = graph.compile({
|
|
233
|
+
circuitBreaker: { threshold: 5, resetAfter: 30_000 },
|
|
234
|
+
});
|
|
235
|
+
```
|
|
184
236
|
|
|
185
|
-
|
|
237
|
+
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.
|
|
186
238
|
|
|
187
|
-
|
|
239
|
+
### Node Timeouts
|
|
188
240
|
|
|
189
241
|
```ts
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const channels = {
|
|
193
|
-
query: lastValue(() => ""), // last write wins
|
|
194
|
-
messages: appendList(() => []), // arrays concatenate
|
|
195
|
-
context: mergeObject(() => ({})), // shallow merge
|
|
196
|
-
scratch: ephemeralValue(() => null), // resets each superstep
|
|
197
|
-
};
|
|
242
|
+
graph.addNode("llm_call", handler, { timeout: 10_000 }); // 10s deadline
|
|
198
243
|
```
|
|
199
244
|
|
|
200
|
-
|
|
245
|
+
Nodes that exceed their timeout are killed and routed through the error recovery path.
|
|
246
|
+
|
|
247
|
+
### Retry with Backoff
|
|
201
248
|
|
|
202
249
|
```ts
|
|
203
|
-
graph.addNode("
|
|
204
|
-
|
|
250
|
+
graph.addNode("flaky_api", handler, {
|
|
251
|
+
retry: { maxAttempts: 3, backoff: "exponential", initialDelay: 1000 },
|
|
205
252
|
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Dead Letter Queue
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
const dlq = new DeadLetterQueue();
|
|
259
|
+
const app = graph.compile({ dlq });
|
|
260
|
+
|
|
261
|
+
// Later: inspect failures
|
|
262
|
+
const failures = dlq.list();
|
|
263
|
+
```
|
|
206
264
|
|
|
207
|
-
|
|
208
|
-
graph.addConditionalEdges("agent", (state) => state.done ? END : "tools");
|
|
265
|
+
### OpenTelemetry Tracing
|
|
209
266
|
|
|
210
|
-
|
|
267
|
+
```ts
|
|
268
|
+
import { ONITracer } from "@oni.bot/core";
|
|
269
|
+
import { trace } from "@opentelemetry/api";
|
|
270
|
+
|
|
271
|
+
const tracer = new ONITracer(trace.getTracer("my-app"));
|
|
272
|
+
const app = graph.compile({ tracer });
|
|
211
273
|
```
|
|
212
274
|
|
|
213
|
-
|
|
275
|
+
Zero-dep adapter that wraps any OpenTelemetry-compatible tracer. Emits spans for graph execution, node runs, tool calls, model invocations, and checkpoint operations.
|
|
276
|
+
|
|
277
|
+
### Checkpointing + Time Travel
|
|
214
278
|
|
|
215
279
|
```ts
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
280
|
+
import { SqliteCheckpointer } from "@oni.bot/core/checkpointers";
|
|
281
|
+
|
|
282
|
+
const checkpointer = new SqliteCheckpointer("./state.db");
|
|
283
|
+
const app = graph.compile({ checkpointer });
|
|
284
|
+
|
|
285
|
+
// Resume from any point
|
|
286
|
+
const history = await app.getHistory("thread-1");
|
|
287
|
+
const forked = await app.forkFrom("thread-1", history[2].checkpoint);
|
|
219
288
|
```
|
|
220
289
|
|
|
221
|
-
|
|
290
|
+
Three built-in backends: `MemoryCheckpointer` (dev), `SqliteCheckpointer` (single-node), `PostgresCheckpointer` (distributed). Or implement `ONICheckpointer` for your own.
|
|
222
291
|
|
|
223
292
|
---
|
|
224
293
|
|
|
225
|
-
##
|
|
294
|
+
## Human-in-the-Loop
|
|
226
295
|
|
|
227
|
-
|
|
296
|
+
```ts
|
|
297
|
+
import { interrupt, getUserApproval } from "@oni.bot/core/hitl";
|
|
298
|
+
|
|
299
|
+
graph.addNode("review", async (state) => {
|
|
300
|
+
const approved = await getUserApproval("Publish this draft?", {
|
|
301
|
+
payload: state.draft,
|
|
302
|
+
});
|
|
303
|
+
if (!approved) return { status: "rejected" };
|
|
304
|
+
return { status: "published" };
|
|
305
|
+
});
|
|
306
|
+
```
|
|
228
307
|
|
|
229
|
-
|
|
230
|
-
|---|---|
|
|
231
|
-
| `@oni.bot/core` | Everything (116+ exports) |
|
|
232
|
-
| `@oni.bot/core/prebuilt` | `createReactAgent`, `createToolNode`, `toolsCondition`, types |
|
|
233
|
-
| `@oni.bot/core/swarm` | `SwarmGraph` (7 templates), `AgentRegistry`, `AgentPool`, `Handoff`, `Supervisor`, `Mailbox`, coordination |
|
|
234
|
-
| `@oni.bot/core/hitl` | `interrupt`, `getUserInput`, `getUserApproval`, `getUserSelection`, session store |
|
|
235
|
-
| `@oni.bot/core/store` | `BaseStore`, `InMemoryStore`, `NamespacedStore`, `AgentMemoryStore` |
|
|
236
|
-
| `@oni.bot/core/messages` | `messagesChannel`, `messagesReducer`, helpers, `RemoveMessage`, `UpdateMessage` |
|
|
237
|
-
| `@oni.bot/core/checkpointers` | `SqliteCheckpointer`, `PostgresCheckpointer`, `NamespacedCheckpointer` |
|
|
238
|
-
| `@oni.bot/core/functional` | `task`, `entrypoint`, `pipe`, `branch` |
|
|
239
|
-
| `@oni.bot/core/inspect` | `buildGraphDescriptor`, `toMermaidDetailed` |
|
|
240
|
-
| `@oni.bot/core/streaming` | `emitToken`, `TokenStreamWriter`, `StreamWriterImpl` |
|
|
241
|
-
| `@oni.bot/core/models` | `ONIModel`, `anthropic()`, `openai()` model factories |
|
|
242
|
-
| `@oni.bot/core/agents` | `defineAgent`, `agent()`, `AgentContext`, types |
|
|
243
|
-
| `@oni.bot/core/coordination` | `RequestReplyBroker`, `PubSub` coordination primitives |
|
|
244
|
-
| `@oni.bot/core/guardrails` | `InputGuardrail`, `OutputGuardrail`, guardrail types |
|
|
245
|
-
| `@oni.bot/core/tools` | `defineTool`, `createInjectedTool`, tool types |
|
|
308
|
+
Execution pauses, the interrupt is surfaced to your application, and the graph resumes exactly where it left off when the user responds.
|
|
246
309
|
|
|
247
310
|
---
|
|
248
311
|
|
|
249
|
-
##
|
|
312
|
+
## Streaming
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
// Single mode
|
|
316
|
+
for await (const event of app.stream(input, { streamMode: "updates" })) {
|
|
317
|
+
console.log(event.node, event.data);
|
|
318
|
+
}
|
|
250
319
|
|
|
320
|
+
// Multiple modes simultaneously
|
|
321
|
+
for await (const event of app.stream(input, { streamMode: ["values", "messages"] })) {
|
|
322
|
+
if (event.mode === "messages") {
|
|
323
|
+
process.stdout.write(event.data.chunk); // Token-by-token LLM output
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Custom events from inside a node
|
|
328
|
+
import { getStreamWriter } from "@oni.bot/core";
|
|
329
|
+
|
|
330
|
+
graph.addNode("agent", async (state) => {
|
|
331
|
+
const writer = getStreamWriter();
|
|
332
|
+
writer.emit("progress", { step: 1, message: "Searching..." });
|
|
333
|
+
// ...
|
|
334
|
+
});
|
|
251
335
|
```
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
│ ├── Coordination ← RequestReplyBroker, PubSub (lazy auto-wired)
|
|
274
|
-
│ └── Retry ← retry-then-fallback error recovery
|
|
275
|
-
├── Functional ← task, entrypoint, pipe, branch
|
|
276
|
-
├── Prebuilt ← createReactAgent, createToolNode, toolsCondition
|
|
277
|
-
├── Inspect ← graph descriptors, Mermaid, cycle detection
|
|
278
|
-
├── Injected Tools ← createInjectedTool (state + store auto-injected)
|
|
279
|
-
├── Stream Events ← streamEvents v2 protocol
|
|
280
|
-
└── Errors ← ONIError hierarchy, ONIInterrupt
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Functional API
|
|
340
|
+
|
|
341
|
+
For simpler use cases, skip the builder pattern entirely:
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
import { entrypoint, task } from "@oni.bot/core/functional";
|
|
345
|
+
import { lastValue } from "@oni.bot/core";
|
|
346
|
+
|
|
347
|
+
const summarize = task("summarize", async (text: string) => {
|
|
348
|
+
return await llm.chat({ messages: [{ role: "user", content: `Summarize: ${text}` }] });
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const app = entrypoint(
|
|
352
|
+
{ channels: { query: lastValue(() => ""), answer: lastValue(() => "") } },
|
|
353
|
+
async (state) => ({ answer: await summarize(state.query) }),
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
const result = await app.invoke({ query: "Explain quantum computing" });
|
|
281
357
|
```
|
|
282
358
|
|
|
283
359
|
---
|
|
284
360
|
|
|
285
|
-
##
|
|
361
|
+
## Testing
|
|
286
362
|
|
|
287
|
-
|
|
363
|
+
```ts
|
|
364
|
+
import { mockModel, assertGraph, createTestHarness } from "@oni.bot/core/testing";
|
|
365
|
+
|
|
366
|
+
// Deterministic model responses
|
|
367
|
+
const model = mockModel([
|
|
368
|
+
{ role: "assistant", content: "Hello!" },
|
|
369
|
+
{ role: "assistant", content: "Goodbye!", toolCalls: [{ id: "1", name: "search", args: {} }] },
|
|
370
|
+
]);
|
|
371
|
+
|
|
372
|
+
// Structural graph assertions
|
|
373
|
+
assertGraph(graph, {
|
|
374
|
+
hasNode: ["agent", "tools"],
|
|
375
|
+
hasEdge: [["__start__", "agent"]],
|
|
376
|
+
nodeCount: 2,
|
|
377
|
+
});
|
|
288
378
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
| `@oni/oats` | OATS — Five9 AI Agent Assist integration |
|
|
379
|
+
// Pre-wired test harness
|
|
380
|
+
const harness = createTestHarness(graph);
|
|
381
|
+
const result = await harness.invoke({ query: "test" });
|
|
382
|
+
const events = await harness.collectStream({ query: "test" }, "updates");
|
|
383
|
+
```
|
|
295
384
|
|
|
296
385
|
---
|
|
297
386
|
|
|
298
387
|
## Documentation
|
|
299
388
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
389
|
+
| Resource | Description |
|
|
390
|
+
|---|---|
|
|
391
|
+
| **[Developer Guide](docs/GUIDE.md)** | Progressive tutorial from zero to advanced -- 19 sections covering every feature |
|
|
392
|
+
| **[API Reference](docs/API.md)** | Complete reference for all 130+ public exports |
|
|
393
|
+
| **[Examples](examples/)** | 30+ runnable example files covering every feature |
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Optional Peer Dependencies
|
|
398
|
+
|
|
399
|
+
The core engine has zero runtime dependencies. Optional packages unlock additional backends:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
npm install better-sqlite3 # SqliteCheckpointer
|
|
403
|
+
npm install pg # PostgresCheckpointer
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Contributing
|
|
409
|
+
|
|
410
|
+
Contributions are welcome. Please open an issue to discuss significant changes before submitting a PR.
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
git clone https://github.com/oni-bot/core.git
|
|
414
|
+
cd core
|
|
415
|
+
npm install
|
|
416
|
+
npm test
|
|
417
|
+
```
|
|
303
418
|
|
|
304
419
|
---
|
|
305
420
|
|
|
306
421
|
## License
|
|
307
422
|
|
|
308
|
-
MIT
|
|
423
|
+
MIT -- [ONI Platform](https://github.com/oni-bot)
|