@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.
Files changed (129) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +315 -200
  3. package/SECURITY.md +71 -0
  4. package/dist/checkpointers/sqlite.d.ts.map +1 -1
  5. package/dist/checkpointers/sqlite.js +20 -18
  6. package/dist/checkpointers/sqlite.js.map +1 -1
  7. package/dist/circuit-breaker.d.ts +20 -0
  8. package/dist/circuit-breaker.d.ts.map +1 -0
  9. package/dist/circuit-breaker.js +58 -0
  10. package/dist/circuit-breaker.js.map +1 -0
  11. package/dist/cli/index.d.ts +3 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +32 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/init.d.ts +2 -0
  16. package/dist/cli/init.d.ts.map +1 -0
  17. package/dist/cli/init.js +17 -0
  18. package/dist/cli/init.js.map +1 -0
  19. package/dist/cli/templates.d.ts +8 -0
  20. package/dist/cli/templates.d.ts.map +1 -0
  21. package/dist/cli/templates.js +119 -0
  22. package/dist/cli/templates.js.map +1 -0
  23. package/dist/dlq.d.ts +17 -0
  24. package/dist/dlq.d.ts.map +1 -0
  25. package/dist/dlq.js +41 -0
  26. package/dist/dlq.js.map +1 -0
  27. package/dist/errors.d.ts +36 -1
  28. package/dist/errors.d.ts.map +1 -1
  29. package/dist/errors.js +163 -7
  30. package/dist/errors.js.map +1 -1
  31. package/dist/graph.d.ts +17 -0
  32. package/dist/graph.d.ts.map +1 -1
  33. package/dist/graph.js +13 -2
  34. package/dist/graph.js.map +1 -1
  35. package/dist/harness/agent-loop.d.ts +8 -0
  36. package/dist/harness/agent-loop.d.ts.map +1 -0
  37. package/dist/harness/agent-loop.js +327 -0
  38. package/dist/harness/agent-loop.js.map +1 -0
  39. package/dist/harness/context-compactor.d.ts +73 -0
  40. package/dist/harness/context-compactor.d.ts.map +1 -0
  41. package/dist/harness/context-compactor.js +162 -0
  42. package/dist/harness/context-compactor.js.map +1 -0
  43. package/dist/harness/harness.d.ts +41 -0
  44. package/dist/harness/harness.d.ts.map +1 -0
  45. package/dist/harness/harness.js +140 -0
  46. package/dist/harness/harness.js.map +1 -0
  47. package/dist/harness/hooks-engine.d.ts +71 -0
  48. package/dist/harness/hooks-engine.d.ts.map +1 -0
  49. package/dist/harness/hooks-engine.js +232 -0
  50. package/dist/harness/hooks-engine.js.map +1 -0
  51. package/dist/harness/index.d.ts +16 -0
  52. package/dist/harness/index.d.ts.map +1 -0
  53. package/dist/harness/index.js +19 -0
  54. package/dist/harness/index.js.map +1 -0
  55. package/dist/harness/safety-gate.d.ts +29 -0
  56. package/dist/harness/safety-gate.d.ts.map +1 -0
  57. package/dist/harness/safety-gate.js +72 -0
  58. package/dist/harness/safety-gate.js.map +1 -0
  59. package/dist/harness/skill-loader.d.ts +63 -0
  60. package/dist/harness/skill-loader.d.ts.map +1 -0
  61. package/dist/harness/skill-loader.js +214 -0
  62. package/dist/harness/skill-loader.js.map +1 -0
  63. package/dist/harness/todo-module.d.ts +39 -0
  64. package/dist/harness/todo-module.d.ts.map +1 -0
  65. package/dist/harness/todo-module.js +179 -0
  66. package/dist/harness/todo-module.js.map +1 -0
  67. package/dist/harness/types.d.ts +78 -0
  68. package/dist/harness/types.d.ts.map +1 -0
  69. package/dist/harness/types.js +9 -0
  70. package/dist/harness/types.js.map +1 -0
  71. package/dist/hitl/interrupt.d.ts.map +1 -1
  72. package/dist/hitl/interrupt.js +7 -6
  73. package/dist/hitl/interrupt.js.map +1 -1
  74. package/dist/index.d.ts +14 -3
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +12 -3
  77. package/dist/index.js.map +1 -1
  78. package/dist/models/google.d.ts.map +1 -1
  79. package/dist/models/google.js +7 -6
  80. package/dist/models/google.js.map +1 -1
  81. package/dist/models/index.d.ts +2 -0
  82. package/dist/models/index.d.ts.map +1 -1
  83. package/dist/models/index.js +1 -0
  84. package/dist/models/index.js.map +1 -1
  85. package/dist/models/openai.js +6 -1
  86. package/dist/models/openai.js.map +1 -1
  87. package/dist/models/openrouter.d.ts +13 -0
  88. package/dist/models/openrouter.d.ts.map +1 -0
  89. package/dist/models/openrouter.js +322 -0
  90. package/dist/models/openrouter.js.map +1 -0
  91. package/dist/prebuilt/tool-node.d.ts.map +1 -1
  92. package/dist/prebuilt/tool-node.js +0 -1
  93. package/dist/prebuilt/tool-node.js.map +1 -1
  94. package/dist/pregel.d.ts +11 -1
  95. package/dist/pregel.d.ts.map +1 -1
  96. package/dist/pregel.js +88 -7
  97. package/dist/pregel.js.map +1 -1
  98. package/dist/retry.d.ts.map +1 -1
  99. package/dist/retry.js +6 -1
  100. package/dist/retry.js.map +1 -1
  101. package/dist/store/index.d.ts +10 -0
  102. package/dist/store/index.d.ts.map +1 -1
  103. package/dist/store/index.js +15 -1
  104. package/dist/store/index.js.map +1 -1
  105. package/dist/stream-events.js +2 -2
  106. package/dist/stream-events.js.map +1 -1
  107. package/dist/streaming.d.ts +10 -0
  108. package/dist/streaming.d.ts.map +1 -1
  109. package/dist/streaming.js +28 -0
  110. package/dist/streaming.js.map +1 -1
  111. package/dist/swarm/graph.d.ts.map +1 -1
  112. package/dist/swarm/graph.js +0 -4
  113. package/dist/swarm/graph.js.map +1 -1
  114. package/dist/swarm/supervisor.d.ts.map +1 -1
  115. package/dist/swarm/supervisor.js +0 -4
  116. package/dist/swarm/supervisor.js.map +1 -1
  117. package/dist/telemetry.d.ts +41 -0
  118. package/dist/telemetry.d.ts.map +1 -0
  119. package/dist/telemetry.js +69 -0
  120. package/dist/telemetry.js.map +1 -0
  121. package/dist/testing/index.d.ts +33 -0
  122. package/dist/testing/index.d.ts.map +1 -0
  123. package/dist/testing/index.js +95 -0
  124. package/dist/testing/index.js.map +1 -0
  125. package/dist/types.d.ts +9 -0
  126. package/dist/types.d.ts.map +1 -1
  127. package/dist/types.js +1 -1
  128. package/dist/types.js.map +1 -1
  129. package/package.json +38 -5
package/README.md CHANGED
@@ -1,57 +1,48 @@
1
- # @oni.bot/core
1
+ <p align="center">
2
+ <strong>@oni.bot/core</strong>
3
+ </p>
2
4
 
3
- > The graph execution engine for agent swarms — build single agents or orchestrate multi-agent systems in TypeScript.
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
- Part of the **ONI Platform** (Open Neural Infrastructure).
7
+ <p align="center">
8
+ Production-grade orchestration with zero dependencies.
9
+ </p>
7
10
 
8
- ---
9
-
10
- ## What It Is
11
-
12
- `@oni.bot/core` is a graph execution framework in TypeScript with production-ready swarm orchestration. Build single agents or multi-agent swarms with:
13
-
14
- - **State management** — typed channels with pluggable reducers (`lastValue`, `appendList`, `mergeObject`, `ephemeralValue`)
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
- ## Installation
21
+ ## Why ONI
31
22
 
32
- ```bash
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
- **Optional peer dependencies:**
25
+ ONI was built for the hard things:
37
26
 
38
- ```bash
39
- npm install better-sqlite3 # for SqliteCheckpointer
40
- npm install pg # for PostgresCheckpointer
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
- ### Minimal graph
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 MyState = { query: string; answer: string; log: string[] };
43
+ type State = { query: string; answer: string; log: string[] };
53
44
 
54
- const graph = new StateGraph<MyState>({
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("agent", async (state) => {
63
- return { answer: `Response to: ${state.query}`, log: ["agent ran"] };
53
+ graph.addNode("think", async (state) => {
54
+ return { answer: `Processed: ${state.query}`, log: ["think ran"] };
64
55
  });
65
56
 
66
- graph.addEdge(START, "agent");
67
- graph.addEdge("agent", END);
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
- ### Streaming
65
+ ---
66
+
67
+ ## Multi-Agent Swarm
68
+
69
+ Build coordinated agent teams with a single function call:
74
70
 
75
71
  ```ts
76
- for await (const event of app.stream({ query: "hello" }, { streamMode: "updates" })) {
77
- console.log(event.node, event.data);
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
- // Multiple modes simultaneously
81
- for await (const evt of app.stream(input, { streamMode: ["values", "messages"] })) {
82
- if (evt.mode === "messages") process.stdout.write(evt.data.chunk);
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
- ### Functional API
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
- ```ts
89
- import { entrypoint, lastValue } from "@oni.bot/core";
100
+ ---
90
101
 
91
- const app = entrypoint(
92
- { channels: { query: lastValue(() => ""), answer: lastValue(() => "") } },
93
- async (state) => ({ answer: await myLLM(state.query) })
94
- );
102
+ ## Features
95
103
 
96
- const result = await app.invoke({ query: "hello" });
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
- Build multi-agent systems with pre-wired templates. Each template configures routing, coordination, and error handling automatically.
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
- ### Hierarchical (Supervisor -> Workers)
158
+ Each template handles routing, error recovery, and termination automatically. See `examples/swarm/` for runnable examples.
106
159
 
107
- ```ts
108
- import { SwarmGraph } from "@oni.bot/core/swarm";
160
+ ---
109
161
 
110
- const swarm = SwarmGraph.hierarchical<MyState>({
111
- supervisor: { model: myModel, strategy: "llm", maxRounds: 10 },
112
- agents: [researcher, writer, critic],
113
- onError: "fallback",
114
- });
162
+ ## Architecture
115
163
 
116
- const app = swarm.compile();
117
- const result = await app.invoke({ task: "Write a blog post about AI" });
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
- ### All 7 Templates
191
+ Entry points at every level of abstraction:
121
192
 
122
- | Template | Use case | Pattern |
123
- |---|---|---|
124
- | `SwarmGraph.hierarchical()` | Supervisor routes to workers | Supervisor -> Agent -> Supervisor -> END |
125
- | `SwarmGraph.fanOut()` | Parallel execution + aggregation | Send to all -> Collect -> Reduce |
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
- See `examples/swarm/` for complete runnable examples of each template.
198
+ ---
133
199
 
134
- ### Single Agent — No Swarm Required
200
+ ## Sub-module Imports
135
201
 
136
- The swarm layer is purely additive. If you just need one agent:
202
+ Tree-shakeable sub-module imports for bundle optimization:
137
203
 
138
204
  ```ts
139
- import { defineAgent } from "@oni.bot/core/agents";
140
- import { anthropic } from "@oni.bot/core/models";
141
-
142
- const agent = defineAgent({
143
- name: "assistant",
144
- model: anthropic("claude-sonnet-4-6"),
145
- tools: [webSearch],
146
- systemPrompt: "You are a helpful assistant.",
147
- });
148
-
149
- const result = await agent.invoke({ messages: [{ role: "user", content: "Hello!" }] });
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
- Entry points at every level: `ONIModel` -> `defineAgent()` -> `StateGraph` -> `SwarmGraph`.
223
+ 16 entry points. Import only what you use.
153
224
 
154
225
  ---
155
226
 
156
- ## Features at a Glance
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
- ## Core Concepts
231
+ ```ts
232
+ const app = graph.compile({
233
+ circuitBreaker: { threshold: 5, resetAfter: 30_000 },
234
+ });
235
+ ```
184
236
 
185
- ### Channels
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
- Every state field has a **channel** — a reducer + default factory that controls how concurrent updates merge.
239
+ ### Node Timeouts
188
240
 
189
241
  ```ts
190
- import { lastValue, appendList, mergeObject, ephemeralValue } from "@oni.bot/core";
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
- ### Nodes, edges, compile
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("agent", async (state, config?) => {
204
- return { answer: "partial update" }; // or Command, or void
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
- graph.addEdge(START, "agent");
208
- graph.addConditionalEdges("agent", (state) => state.done ? END : "tools");
265
+ ### OpenTelemetry Tracing
209
266
 
210
- const app = graph.compile({ checkpointer, store });
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
- ### Invoke / Stream / Batch
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
- const result = await app.invoke(input, { threadId: "t1" });
217
- const stream = app.stream(input, { streamMode: "updates" });
218
- const results = await app.batch([input1, input2]);
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
- For the full progressive tutorial, see **[docs/GUIDE.md](docs/GUIDE.md)**.
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
- ## Sub-module Exports
294
+ ## Human-in-the-Loop
226
295
 
227
- Tree-shakeable sub-module imports for bundle optimization:
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
- | Import path | Contents |
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
- ## Architecture
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
- @oni.bot/core v0.6.0
253
- ├── StateGraph / MessageGraph ← fluent graph builder
254
- │ ├── addNode / addSubgraph ← nodes are async functions or compiled skeletons
255
- │ ├── addEdge / addConditional ← static + dynamic routing
256
- │ └── compile()
257
- │ └── ONIPregelRunner ← superstep execution engine
258
- │ ├── parallel node execution (Promise.all per superstep)
259
- │ ├── channel reducers (state merging)
260
- │ ├── edge resolution + Command routing
261
- │ ├── Send API (dynamic fan-out)
262
- │ ├── checkpointing (Memory / SQLite / Postgres / custom)
263
- │ ├── interrupt handling (boundary + in-node)
264
- │ ├── retry engine (exponential backoff)
265
- │ ├── runtime context (AsyncLocalStorage)
266
- │ └── stream writer (tokens + custom events + messages)
267
-
268
- ├── Messages ← smart reducer, dedup, RemoveMessage, UpdateMessage
269
- ├── HITL ← interrupt(), getUserInput, sessions, resume
270
- ├── Store ← BaseStore, InMemoryStore, NamespacedStore, AgentMemoryStore
271
- ├── Swarm ← SwarmGraph (7 templates), Supervisor, Handoff, AgentPool, coordination
272
- │ ├── Templates ← hierarchical, fanOut, pipeline, peerNetwork, mapReduce, debate, hierarchicalMesh
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
- ## ONI Platform
361
+ ## Testing
286
362
 
287
- `@oni.bot/core` is the foundation layer. Other ONI packages build on top:
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
- | Package | Built on |
290
- |---|---|
291
- | `@oni/agentOS` | AgentOS ADE multi-agent orchestration |
292
- | `@oni/vectorforge` | VectorForge knowledge base & SOP retrieval |
293
- | `@oni/cic` | CIC Agent Assist — call center intelligence |
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
- - **[Developer Guide](docs/GUIDE.md)** Progressive tutorial from zero to advanced (19 sections)
301
- - **[API Reference](docs/API.md)** — Complete reference for all 116+ public exports
302
- - **[Examples](examples/)** 30+ runnable example files covering every feature
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 ONI Platform
423
+ MIT -- [ONI Platform](https://github.com/oni-bot)