@hazeljs/agent 0.2.0-alpha.1

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 (166) hide show
  1. package/IMPLEMENTATION_SUMMARY.md +400 -0
  2. package/LICENSE +192 -0
  3. package/PERSISTENCE.md +201 -0
  4. package/PRISMA_INTEGRATION.md +499 -0
  5. package/PRODUCTION_READINESS.md +264 -0
  6. package/QUICKSTART.md +135 -0
  7. package/README.md +541 -0
  8. package/STATE_VS_MEMORY.md +243 -0
  9. package/benchmarks/performance.benchmark.ts +157 -0
  10. package/coverage/clover.xml +1508 -0
  11. package/coverage/lcov-report/base.css +224 -0
  12. package/coverage/lcov-report/block-navigation.js +87 -0
  13. package/coverage/lcov-report/favicon.png +0 -0
  14. package/coverage/lcov-report/index.html +296 -0
  15. package/coverage/lcov-report/prettify.css +1 -0
  16. package/coverage/lcov-report/prettify.js +2 -0
  17. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  18. package/coverage/lcov-report/sorter.js +210 -0
  19. package/coverage/lcov.info +2722 -0
  20. package/dist/agent.module.d.ts +57 -0
  21. package/dist/agent.module.d.ts.map +1 -0
  22. package/dist/agent.module.js +99 -0
  23. package/dist/agent.module.js.map +1 -0
  24. package/dist/context/agent.context.d.ts +27 -0
  25. package/dist/context/agent.context.d.ts.map +1 -0
  26. package/dist/context/agent.context.js +98 -0
  27. package/dist/context/agent.context.js.map +1 -0
  28. package/dist/decorators/agent.decorator.d.ts +21 -0
  29. package/dist/decorators/agent.decorator.d.ts.map +1 -0
  30. package/dist/decorators/agent.decorator.js +38 -0
  31. package/dist/decorators/agent.decorator.js.map +1 -0
  32. package/dist/decorators/delegate.decorator.d.ts +66 -0
  33. package/dist/decorators/delegate.decorator.d.ts.map +1 -0
  34. package/dist/decorators/delegate.decorator.js +108 -0
  35. package/dist/decorators/delegate.decorator.js.map +1 -0
  36. package/dist/decorators/tool.decorator.d.ts +23 -0
  37. package/dist/decorators/tool.decorator.d.ts.map +1 -0
  38. package/dist/decorators/tool.decorator.js +61 -0
  39. package/dist/decorators/tool.decorator.js.map +1 -0
  40. package/dist/events/event.emitter.d.ts +45 -0
  41. package/dist/events/event.emitter.d.ts.map +1 -0
  42. package/dist/events/event.emitter.js +96 -0
  43. package/dist/events/event.emitter.js.map +1 -0
  44. package/dist/executor/agent.executor.d.ts +58 -0
  45. package/dist/executor/agent.executor.d.ts.map +1 -0
  46. package/dist/executor/agent.executor.js +312 -0
  47. package/dist/executor/agent.executor.js.map +1 -0
  48. package/dist/executor/tool.executor.d.ts +55 -0
  49. package/dist/executor/tool.executor.d.ts.map +1 -0
  50. package/dist/executor/tool.executor.js +266 -0
  51. package/dist/executor/tool.executor.js.map +1 -0
  52. package/dist/graph/agent-graph.d.ts +131 -0
  53. package/dist/graph/agent-graph.d.ts.map +1 -0
  54. package/dist/graph/agent-graph.js +462 -0
  55. package/dist/graph/agent-graph.js.map +1 -0
  56. package/dist/graph/agent-graph.types.d.ts +210 -0
  57. package/dist/graph/agent-graph.types.d.ts.map +1 -0
  58. package/dist/graph/agent-graph.types.js +12 -0
  59. package/dist/graph/agent-graph.types.js.map +1 -0
  60. package/dist/index.d.ts +34 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +51 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/prompts/agent-system.prompt.d.ts +10 -0
  65. package/dist/prompts/agent-system.prompt.d.ts.map +1 -0
  66. package/dist/prompts/agent-system.prompt.js +18 -0
  67. package/dist/prompts/agent-system.prompt.js.map +1 -0
  68. package/dist/prompts/index.d.ts +4 -0
  69. package/dist/prompts/index.d.ts.map +1 -0
  70. package/dist/prompts/index.js +20 -0
  71. package/dist/prompts/index.js.map +1 -0
  72. package/dist/prompts/supervisor-routing.prompt.d.ts +9 -0
  73. package/dist/prompts/supervisor-routing.prompt.d.ts.map +1 -0
  74. package/dist/prompts/supervisor-routing.prompt.js +22 -0
  75. package/dist/prompts/supervisor-routing.prompt.js.map +1 -0
  76. package/dist/prompts/supervisor-system.prompt.d.ts +9 -0
  77. package/dist/prompts/supervisor-system.prompt.d.ts.map +1 -0
  78. package/dist/prompts/supervisor-system.prompt.js +21 -0
  79. package/dist/prompts/supervisor-system.prompt.js.map +1 -0
  80. package/dist/registry/agent.registry.d.ts +49 -0
  81. package/dist/registry/agent.registry.d.ts.map +1 -0
  82. package/dist/registry/agent.registry.js +90 -0
  83. package/dist/registry/agent.registry.js.map +1 -0
  84. package/dist/registry/tool.registry.d.ts +54 -0
  85. package/dist/registry/tool.registry.d.ts.map +1 -0
  86. package/dist/registry/tool.registry.js +153 -0
  87. package/dist/registry/tool.registry.js.map +1 -0
  88. package/dist/runtime/agent.runtime.d.ts +218 -0
  89. package/dist/runtime/agent.runtime.d.ts.map +1 -0
  90. package/dist/runtime/agent.runtime.extensions.d.ts +49 -0
  91. package/dist/runtime/agent.runtime.extensions.d.ts.map +1 -0
  92. package/dist/runtime/agent.runtime.extensions.js +93 -0
  93. package/dist/runtime/agent.runtime.extensions.js.map +1 -0
  94. package/dist/runtime/agent.runtime.js +448 -0
  95. package/dist/runtime/agent.runtime.js.map +1 -0
  96. package/dist/state/agent-state.interface.d.ts +63 -0
  97. package/dist/state/agent-state.interface.d.ts.map +1 -0
  98. package/dist/state/agent-state.interface.js +7 -0
  99. package/dist/state/agent-state.interface.js.map +1 -0
  100. package/dist/state/agent.state.d.ts +67 -0
  101. package/dist/state/agent.state.d.ts.map +1 -0
  102. package/dist/state/agent.state.js +172 -0
  103. package/dist/state/agent.state.js.map +1 -0
  104. package/dist/state/database-state.manager.d.ts +63 -0
  105. package/dist/state/database-state.manager.d.ts.map +1 -0
  106. package/dist/state/database-state.manager.js +282 -0
  107. package/dist/state/database-state.manager.js.map +1 -0
  108. package/dist/state/redis-state.manager.d.ts +81 -0
  109. package/dist/state/redis-state.manager.d.ts.map +1 -0
  110. package/dist/state/redis-state.manager.js +253 -0
  111. package/dist/state/redis-state.manager.js.map +1 -0
  112. package/dist/supervisor/supervisor.d.ts +81 -0
  113. package/dist/supervisor/supervisor.d.ts.map +1 -0
  114. package/dist/supervisor/supervisor.js +220 -0
  115. package/dist/supervisor/supervisor.js.map +1 -0
  116. package/dist/types/agent.types.d.ts +166 -0
  117. package/dist/types/agent.types.d.ts.map +1 -0
  118. package/dist/types/agent.types.js +32 -0
  119. package/dist/types/agent.types.js.map +1 -0
  120. package/dist/types/event.types.d.ts +198 -0
  121. package/dist/types/event.types.d.ts.map +1 -0
  122. package/dist/types/event.types.js +46 -0
  123. package/dist/types/event.types.js.map +1 -0
  124. package/dist/types/llm.types.d.ts +66 -0
  125. package/dist/types/llm.types.d.ts.map +1 -0
  126. package/dist/types/llm.types.js +7 -0
  127. package/dist/types/llm.types.js.map +1 -0
  128. package/dist/types/rag.types.d.ts +40 -0
  129. package/dist/types/rag.types.d.ts.map +1 -0
  130. package/dist/types/rag.types.js +7 -0
  131. package/dist/types/rag.types.js.map +1 -0
  132. package/dist/types/tool.types.d.ts +118 -0
  133. package/dist/types/tool.types.d.ts.map +1 -0
  134. package/dist/types/tool.types.js +19 -0
  135. package/dist/types/tool.types.js.map +1 -0
  136. package/dist/utils/circuit-breaker.d.ts +9 -0
  137. package/dist/utils/circuit-breaker.d.ts.map +1 -0
  138. package/dist/utils/circuit-breaker.js +16 -0
  139. package/dist/utils/circuit-breaker.js.map +1 -0
  140. package/dist/utils/health-check.d.ts +71 -0
  141. package/dist/utils/health-check.d.ts.map +1 -0
  142. package/dist/utils/health-check.js +156 -0
  143. package/dist/utils/health-check.js.map +1 -0
  144. package/dist/utils/logger.d.ts +53 -0
  145. package/dist/utils/logger.d.ts.map +1 -0
  146. package/dist/utils/logger.js +133 -0
  147. package/dist/utils/logger.js.map +1 -0
  148. package/dist/utils/metrics.d.ts +90 -0
  149. package/dist/utils/metrics.d.ts.map +1 -0
  150. package/dist/utils/metrics.js +186 -0
  151. package/dist/utils/metrics.js.map +1 -0
  152. package/dist/utils/rate-limiter.d.ts +44 -0
  153. package/dist/utils/rate-limiter.d.ts.map +1 -0
  154. package/dist/utils/rate-limiter.js +82 -0
  155. package/dist/utils/rate-limiter.js.map +1 -0
  156. package/dist/utils/retry.d.ts +42 -0
  157. package/dist/utils/retry.d.ts.map +1 -0
  158. package/dist/utils/retry.js +103 -0
  159. package/dist/utils/retry.js.map +1 -0
  160. package/jest.config.js +30 -0
  161. package/logs/combined.log +1 -0
  162. package/logs/error.log +0 -0
  163. package/package.json +74 -0
  164. package/prisma-schema.example.prisma +76 -0
  165. package/tests/setup.ts +17 -0
  166. package/tsconfig.tsbuildinfo +1 -0
package/README.md ADDED
@@ -0,0 +1,541 @@
1
+ # @hazeljs/agent
2
+
3
+ **Build AI agents that actually do things.**
4
+
5
+ Stateful, tool-using, memory-enabled. Define tools with `@Tool`, delegate between agents with `@Delegate`, orchestrate multi-agent pipelines with `AgentGraph`, and route tasks automatically with `SupervisorAgent`. Production-grade agent infrastructure without the complexity.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@hazeljs/agent.svg)](https://www.npmjs.com/package/@hazeljs/agent)
8
+ [![npm downloads](https://img.shields.io/npm/dm/@hazeljs/agent)](https://www.npmjs.com/package/@hazeljs/agent)
9
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
10
+
11
+ ## Overview
12
+
13
+ Unlike stateless request handlers, agents are:
14
+
15
+ - **Stateful** — Maintain context across multiple steps and sessions
16
+ - **Long-running** — Execute complex, multi-hop workflows over time
17
+ - **Tool-using** — Call functions safely with timeout, retry, and approval workflows
18
+ - **Multi-agent** — Orchestrate teams of specialised agents with `AgentGraph`, `SupervisorAgent`, and `@Delegate`
19
+ - **Memory-enabled** — Integrate with persistent memory systems
20
+ - **Observable** — Full event system for monitoring and debugging
21
+ - **Resumable** — Support pause/resume and human-in-the-loop
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install @hazeljs/agent @hazeljs/core @hazeljs/rag
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Quick Start — Single Agent
34
+
35
+ ### 1. Define an agent
36
+
37
+ ```typescript
38
+ import { Agent, Tool } from '@hazeljs/agent';
39
+
40
+ @Agent({
41
+ name: 'support-agent',
42
+ description: 'Customer support agent',
43
+ systemPrompt: 'You are a helpful customer support agent.',
44
+ enableMemory: true,
45
+ enableRAG: true,
46
+ })
47
+ export class SupportAgent {
48
+ @Tool({
49
+ description: 'Look up order information by order ID',
50
+ parameters: [{ name: 'orderId', type: 'string', description: 'The order ID', required: true }],
51
+ })
52
+ async lookupOrder(input: { orderId: string }) {
53
+ return { orderId: input.orderId, status: 'shipped', trackingNumber: 'TRACK123' };
54
+ }
55
+
56
+ @Tool({
57
+ description: 'Process a refund for an order',
58
+ requiresApproval: true, // requires human approval before execution
59
+ parameters: [
60
+ { name: 'orderId', type: 'string', required: true },
61
+ { name: 'amount', type: 'number', required: true },
62
+ ],
63
+ })
64
+ async processRefund(input: { orderId: string; amount: number }) {
65
+ return { success: true, refundId: 'REF123', amount: input.amount };
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### 2. Set up the runtime
71
+
72
+ ```typescript
73
+ import { AgentRuntime } from '@hazeljs/agent';
74
+ import { MemoryManager } from '@hazeljs/rag';
75
+ import { AIService } from '@hazeljs/ai';
76
+
77
+ const runtime = new AgentRuntime({
78
+ memoryManager: new MemoryManager(/* ... */),
79
+ llmProvider: new AIService({ provider: 'openai' }),
80
+ defaultMaxSteps: 10,
81
+ enableObservability: true,
82
+ });
83
+
84
+ const agent = new SupportAgent();
85
+ runtime.registerAgent(SupportAgent);
86
+ runtime.registerAgentInstance('support-agent', agent);
87
+ ```
88
+
89
+ ### 3. Execute
90
+
91
+ ```typescript
92
+ const result = await runtime.execute(
93
+ 'support-agent',
94
+ 'I need to check my order #12345',
95
+ { sessionId: 'user-session-123', userId: 'user-456', enableMemory: true },
96
+ );
97
+
98
+ console.log(result.response);
99
+ console.log(`Completed in ${result.steps.length} steps`);
100
+ ```
101
+
102
+ ### 4. Handle human-in-the-loop
103
+
104
+ ```typescript
105
+ runtime.on('tool.approval.requested', async (event) => {
106
+ console.log('Approval needed:', event.data);
107
+ runtime.approveToolExecution(event.data.requestId, 'admin-user');
108
+ });
109
+
110
+ const resumed = await runtime.resume(result.executionId);
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Multi-Agent Orchestration
116
+
117
+ `@hazeljs/agent` ships three complementary patterns for coordinating multiple agents. Use them individually or combine them.
118
+
119
+ ### Pattern 1 — `@Delegate`: peer-to-peer agent calls
120
+
121
+ `@Delegate` marks a method on an agent as a delegation point to another agent. The method body is replaced at runtime with an actual `runtime.execute(targetAgent, input)` call — making agent-to-agent communication completely transparent to the LLM (it sees delegation targets as ordinary tools).
122
+
123
+ ```
124
+ OrchestratorAgent
125
+ └── @Delegate → ResearchAgent
126
+ └── @Delegate → WriterAgent
127
+ ```
128
+
129
+ ```typescript
130
+ import { Agent, Delegate } from '@hazeljs/agent';
131
+
132
+ @Agent({
133
+ name: 'OrchestratorAgent',
134
+ description: 'Plans and delegates research and writing tasks',
135
+ systemPrompt: 'You orchestrate research and writing. Use the available tools to complete tasks.',
136
+ })
137
+ export class OrchestratorAgent {
138
+ // The LLM sees this as a tool. At runtime it calls ResearchAgent.
139
+ @Delegate({
140
+ agent: 'ResearchAgent',
141
+ description: 'Research a topic thoroughly and return key findings',
142
+ inputField: 'query',
143
+ })
144
+ async researchTopic(query: string): Promise<string> {
145
+ return ''; // body replaced at runtime by AgentRuntime
146
+ }
147
+
148
+ // The LLM sees this as a tool. At runtime it calls WriterAgent.
149
+ @Delegate({
150
+ agent: 'WriterAgent',
151
+ description: 'Write a polished article from the provided research notes',
152
+ inputField: 'content',
153
+ })
154
+ async writeArticle(content: string): Promise<string> {
155
+ return ''; // body replaced at runtime by AgentRuntime
156
+ }
157
+ }
158
+
159
+ @Agent({ name: 'ResearchAgent', systemPrompt: 'You are an expert researcher.' })
160
+ export class ResearchAgent {
161
+ @Tool({ description: 'Search the web', parameters: [{ name: 'query', type: 'string', required: true }] })
162
+ async searchWeb(input: { query: string }) {
163
+ return `Research findings for: ${input.query}`;
164
+ }
165
+ }
166
+
167
+ @Agent({ name: 'WriterAgent', systemPrompt: 'You are a professional technical writer.' })
168
+ export class WriterAgent {
169
+ @Tool({ description: 'Format content as Markdown', parameters: [{ name: 'raw', type: 'string', required: true }] })
170
+ async formatMarkdown(input: { raw: string }) {
171
+ return `## Article\n\n${input.raw}`;
172
+ }
173
+ }
174
+ ```
175
+
176
+ **Registration:**
177
+
178
+ ```typescript
179
+ const orchestrator = new OrchestratorAgent();
180
+ const researcher = new ResearchAgent();
181
+ const writer = new WriterAgent();
182
+
183
+ [ResearchAgent, WriterAgent, OrchestratorAgent].forEach(A => runtime.registerAgent(A));
184
+ [['OrchestratorAgent', orchestrator], ['ResearchAgent', researcher], ['WriterAgent', writer]]
185
+ .forEach(([name, inst]) => runtime.registerAgentInstance(name as string, inst));
186
+
187
+ const result = await runtime.execute('OrchestratorAgent', 'Write a blog post about LLMs');
188
+ console.log(result.response);
189
+ ```
190
+
191
+ > **Note:** `@Delegate` implicitly registers the method as `@Tool`. Do not add `@Tool` separately.
192
+
193
+ ---
194
+
195
+ ### Pattern 2 — `AgentGraph`: DAG pipelines
196
+
197
+ `AgentGraph` lets you wire agents and functions into a directed acyclic graph with sequential edges, conditional routing, and parallel fan-out/fan-in. Think LangGraph but TypeScript-native and integrated with `AgentRuntime`.
198
+
199
+ ```
200
+ Entry → NodeA → NodeB → END (sequential)
201
+ Entry → RouterNode → NodeA | NodeB → END (conditional)
202
+ Entry → Splitter → [NodeA ‖ NodeB] → Combiner → END (parallel)
203
+ ```
204
+
205
+ ```typescript
206
+ import { END } from '@hazeljs/agent';
207
+
208
+ // Create graph via the runtime
209
+ const graph = runtime.createGraph('research-pipeline');
210
+ ```
211
+
212
+ #### Sequential pipeline
213
+
214
+ ```typescript
215
+ const pipeline = runtime
216
+ .createGraph('blog-pipeline')
217
+ .addNode('researcher', { type: 'agent', agentName: 'ResearchAgent' })
218
+ .addNode('writer', { type: 'agent', agentName: 'WriterAgent' })
219
+ .addEdge('researcher', 'writer')
220
+ .addEdge('writer', END)
221
+ .setEntryPoint('researcher')
222
+ .compile();
223
+
224
+ const result = await pipeline.execute('Write a blog post about TypeScript generics');
225
+ console.log(result.output);
226
+ ```
227
+
228
+ #### Conditional routing
229
+
230
+ ```typescript
231
+ const router = runtime
232
+ .createGraph('router')
233
+ .addNode('classifier', { type: 'agent', agentName: 'ClassifierAgent' })
234
+ .addNode('coder', { type: 'agent', agentName: 'CoderAgent' })
235
+ .addNode('writer', { type: 'agent', agentName: 'WriterAgent' })
236
+ .setEntryPoint('classifier')
237
+ .addConditionalEdge('classifier', (state) =>
238
+ state.data?.type === 'code' ? 'coder' : 'writer',
239
+ )
240
+ .addEdge('coder', END)
241
+ .addEdge('writer', END)
242
+ .compile();
243
+
244
+ const result = await router.execute('Write a sorting algorithm in TypeScript');
245
+ ```
246
+
247
+ #### Parallel fan-out / fan-in
248
+
249
+ ```typescript
250
+ async function splitTask(state: GraphState) {
251
+ return { ...state, data: { ...state.data, split: true } };
252
+ }
253
+
254
+ async function mergeResults(state: GraphState) {
255
+ const results = state.data?.branchResults as ParallelBranchResult[];
256
+ return { ...state, output: results.map(r => r.output).join('\n---\n') };
257
+ }
258
+
259
+ const parallel = runtime
260
+ .createGraph('parallel-research')
261
+ .addNode('splitter', { type: 'function', fn: splitTask })
262
+ .addNode('parallel-1', { type: 'parallel', branches: ['tech-researcher', 'market-researcher'] })
263
+ .addNode('tech-researcher', { type: 'agent', agentName: 'TechResearchAgent' })
264
+ .addNode('market-researcher', { type: 'agent', agentName: 'MarketResearchAgent' })
265
+ .addNode('combiner', { type: 'function', fn: mergeResults })
266
+ .addEdge('splitter', 'parallel-1')
267
+ .addEdge('parallel-1', 'combiner')
268
+ .addEdge('combiner', END)
269
+ .setEntryPoint('splitter')
270
+ .compile();
271
+
272
+ const result = await parallel.execute('Analyse the AI framework market');
273
+ ```
274
+
275
+ #### Streaming execution
276
+
277
+ ```typescript
278
+ for await (const chunk of pipeline.stream('Tell me about GraphRAG')) {
279
+ if (chunk.type === 'node_complete') {
280
+ console.log(`✓ ${chunk.nodeId}: ${chunk.output?.slice(0, 80)}...`);
281
+ }
282
+ }
283
+ ```
284
+
285
+ #### `AgentGraph` API
286
+
287
+ ```typescript
288
+ interface AgentGraph {
289
+ addNode(id: string, config: GraphNodeConfig): this;
290
+ addEdge(from: string, to: string): this;
291
+ addConditionalEdge(from: string, router: RouterFunction): this;
292
+ setEntryPoint(nodeId: string): this;
293
+ compile(): CompiledGraph;
294
+ }
295
+
296
+ interface CompiledGraph {
297
+ execute(input: string, options?: GraphExecutionOptions): Promise<GraphExecutionResult>;
298
+ stream(input: string, options?: GraphExecutionOptions): AsyncIterable<GraphStreamChunk>;
299
+ visualize(): string; // returns a Mermaid diagram string
300
+ }
301
+ ```
302
+
303
+ ---
304
+
305
+ ### Pattern 3 — `SupervisorAgent`: LLM-driven routing
306
+
307
+ `SupervisorAgent` uses an LLM to decompose tasks into subtasks, route each subtask to the best worker agent, and accumulate results — continuing until the task is complete or `maxRounds` is reached.
308
+
309
+ ```
310
+ User Task
311
+
312
+ Supervisor ←──────────────────────────┐
313
+ │ │
314
+ ┌───▼────────────────┐ Worker result
315
+ │ Route to worker? │ │
316
+ └───────────┬────────┘ │
317
+ │ │
318
+ ┌──────▼──────┐ │
319
+ │ WorkerAgent │───────────────────┘
320
+ └─────────────┘
321
+ ```
322
+
323
+ ```typescript
324
+ const supervisor = runtime.createSupervisor({
325
+ name: 'project-manager',
326
+ workers: ['ResearchAgent', 'CoderAgent', 'WriterAgent'],
327
+ maxRounds: 6,
328
+ llm: async (prompt) => {
329
+ const res = await openai.chat.completions.create({
330
+ model: 'gpt-4o-mini',
331
+ messages: [{ role: 'user', content: prompt }],
332
+ });
333
+ return res.choices[0].message.content ?? '';
334
+ },
335
+ });
336
+
337
+ const result = await supervisor.run(
338
+ 'Build and document a REST API for a todo app',
339
+ { sessionId: 'proj-001' },
340
+ );
341
+
342
+ console.log(result.response);
343
+ result.rounds.forEach((round, i) => {
344
+ console.log(`Round ${i + 1}: routed to ${round.worker} — ${round.workerResult.response.slice(0, 80)}`);
345
+ });
346
+ ```
347
+
348
+ **`SupervisorConfig`:**
349
+
350
+ | Field | Type | Default | Description |
351
+ |-------|------|---------|-------------|
352
+ | `name` | string | — | Supervisor instance name |
353
+ | `workers` | string[] | — | Registered agent names available to the supervisor |
354
+ | `maxRounds` | number | 5 | Maximum routing iterations |
355
+ | `llm` | `(prompt: string) => Promise<string>` | — | LLM function for routing decisions |
356
+ | `sessionId` | string | auto | Session for memory continuity across rounds |
357
+
358
+ ---
359
+
360
+ ## Architecture
361
+
362
+ ```
363
+ ┌──────────────────────────────────────────────────────────────────┐
364
+ │ AgentRuntime │
365
+ ├──────────────┬───────────────┬───────────────┬───────────────────┤
366
+ │ Registry │ State Mgr │ Executor │ Tool Executor │
367
+ │ (agents, │ (in-mem / │ (step loop, │ (timeout, │
368
+ │ tools) │ Redis / DB) │ approval) │ retry, audit) │
369
+ ├──────────────┴───────────────┴───────────────┴───────────────────┤
370
+ │ Multi-Agent Layer │
371
+ │ ┌──────────────┐ ┌───────────────────┐ ┌────────────────────┐ │
372
+ │ │ AgentGraph │ │ SupervisorAgent │ │ @Delegate │ │
373
+ │ │ (DAG pipes) │ │ (LLM routing) │ │ (peer-to-peer) │ │
374
+ │ └──────────────┘ └───────────────────┘ └────────────────────┘ │
375
+ ├────────────────────────────────────────────────────────────────── ┤
376
+ │ Memory Module (@hazeljs/rag) │ RAG Module │
377
+ └──────────────────────────────────────────────────────────────────┘
378
+ ```
379
+
380
+ ---
381
+
382
+ ## State Machine
383
+
384
+ Every agent execution follows a deterministic state machine:
385
+
386
+ ```
387
+ idle → thinking → using_tool → thinking → ... → completed
388
+
389
+ waiting_for_input
390
+
391
+ waiting_for_approval
392
+
393
+ failed
394
+ ```
395
+
396
+ ---
397
+
398
+ ## Event System
399
+
400
+ ```typescript
401
+ import { AgentEventType } from '@hazeljs/agent';
402
+
403
+ runtime.on(AgentEventType.EXECUTION_STARTED, e => console.log('started:', e.data));
404
+ runtime.on(AgentEventType.EXECUTION_COMPLETED, e => console.log('completed:', e.data));
405
+ runtime.on(AgentEventType.STEP_STARTED, e => console.log('step:', e.data));
406
+ runtime.on(AgentEventType.TOOL_EXECUTION_STARTED, e => console.log('tool:', e.data));
407
+ runtime.on(AgentEventType.TOOL_APPROVAL_REQUESTED, e => {
408
+ console.log('approval needed:', e.data);
409
+ runtime.approveToolExecution(e.data.requestId, 'admin');
410
+ });
411
+
412
+ // Catch-all
413
+ runtime.onAny(e => console.log(e.type, e.data));
414
+ ```
415
+
416
+ ---
417
+
418
+ ## HazelJS Module Integration
419
+
420
+ ```typescript
421
+ import { HazelModule } from '@hazeljs/core';
422
+ import { AgentModule } from '@hazeljs/agent';
423
+ import { RagModule } from '@hazeljs/rag';
424
+
425
+ @HazelModule({
426
+ imports: [
427
+ RagModule.forRoot({ /* ... */ }),
428
+ AgentModule.forRoot({
429
+ runtime: { defaultMaxSteps: 10, enableObservability: true },
430
+ agents: [SupportAgent, ResearchAgent, WriterAgent, OrchestratorAgent],
431
+ }),
432
+ ],
433
+ })
434
+ export class AppModule {}
435
+ ```
436
+
437
+ ---
438
+
439
+ ## Best Practices
440
+
441
+ ### Keep tools idempotent
442
+
443
+ ```typescript
444
+ @Tool({ description: 'Create an order' })
445
+ async createOrder(input: { orderId: string; items: Item[] }) {
446
+ const existing = await this.findOrder(input.orderId);
447
+ if (existing) return existing; // safe to retry
448
+ return this.createNewOrder(input);
449
+ }
450
+ ```
451
+
452
+ ### Use `@Delegate` for domain specialisation
453
+
454
+ Keep each agent focused on one domain. `@Delegate` lets the orchestrator combine specialists without any agent becoming a monolith.
455
+
456
+ ### Choose the right multi-agent pattern
457
+
458
+ | Pattern | Use when |
459
+ |---------|----------|
460
+ | `@Delegate` | Two or three agents with a clear orchestrator / worker split |
461
+ | `AgentGraph` | Workflow is known at design time; conditional routing matters |
462
+ | `SupervisorAgent` | Task decomposition is dynamic; you want LLM-driven routing |
463
+
464
+ ### Require approval for destructive actions
465
+
466
+ ```typescript
467
+ @Tool({ requiresApproval: true, description: 'Delete user account' })
468
+ async deleteAccount(input: { userId: string }) { /* ... */ }
469
+ ```
470
+
471
+ ### Handle errors in tools
472
+
473
+ ```typescript
474
+ @Tool({ description: 'Call external API' })
475
+ async callExternalAPI(input: { endpoint: string }) {
476
+ try {
477
+ return await this.api.call(input.endpoint);
478
+ } catch (error) {
479
+ return { success: false, error: (error as Error).message };
480
+ }
481
+ }
482
+ ```
483
+
484
+ ---
485
+
486
+ ## API Reference
487
+
488
+ ### `AgentRuntime`
489
+
490
+ ```typescript
491
+ class AgentRuntime {
492
+ execute(agentName: string, input: string, options?: ExecuteOptions): Promise<AgentExecutionResult>;
493
+ resume(executionId: string, input?: string): Promise<AgentExecutionResult>;
494
+ registerAgent(agentClass: new (...args: unknown[]) => unknown): void;
495
+ registerAgentInstance(name: string, instance: unknown): void;
496
+ createGraph(name: string): AgentGraph;
497
+ createSupervisor(config: SupervisorConfig): SupervisorAgent;
498
+ approveToolExecution(requestId: string, approvedBy: string): void;
499
+ rejectToolExecution(requestId: string, reason?: string): void;
500
+ on(event: string, handler: (e: AgentEvent) => void): void;
501
+ onAny(handler: (e: AgentEvent) => void): void;
502
+ }
503
+ ```
504
+
505
+ ### Decorators
506
+
507
+ | Decorator | Target | Description |
508
+ |-----------|--------|-------------|
509
+ | `@Agent(config)` | Class | Declares a class as an agent |
510
+ | `@Tool(config)` | Method | Exposes a method as an LLM-callable tool |
511
+ | `@Delegate(config)` | Method | Delegates a method to another agent (registers as `@Tool` automatically) |
512
+
513
+ ### `GraphNodeConfig` types
514
+
515
+ ```typescript
516
+ // Agent node — runs a registered agent
517
+ { type: 'agent', agentName: string }
518
+
519
+ // Function node — runs a custom function
520
+ { type: 'function', fn: (state: GraphState) => Promise<GraphState> }
521
+
522
+ // Parallel node — fans out to multiple branches simultaneously
523
+ { type: 'parallel', branches: string[] }
524
+ ```
525
+
526
+ ---
527
+
528
+ ## Examples
529
+
530
+ - [hazeljs-ai-multiagent-starter](../../hazeljs-ai-multiagent-starter) — Full multi-agent REST API with `AgentGraph`, `SupervisorAgent`, and `@Delegate`
531
+ - [hazeljs-rag-documents-starter](../../hazeljs-rag-documents-starter) — RAG + GraphRAG knowledge base API
532
+
533
+ ---
534
+
535
+ ## License
536
+
537
+ Apache 2.0
538
+
539
+ ## Contributing
540
+
541
+ Contributions are welcome! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for details.