@hazeljs/agent 0.2.0-beta.8 → 0.2.0-beta.80

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 (71) hide show
  1. package/LICENSE +192 -21
  2. package/README.md +372 -258
  3. package/coverage/clover.xml +853 -576
  4. package/coverage/lcov-report/index.html +107 -62
  5. package/coverage/lcov.info +1428 -945
  6. package/dist/agent.module.d.ts +16 -1
  7. package/dist/agent.module.d.ts.map +1 -1
  8. package/dist/agent.module.js +14 -5
  9. package/dist/agent.module.js.map +1 -1
  10. package/dist/decorators/delegate.decorator.d.ts +66 -0
  11. package/dist/decorators/delegate.decorator.d.ts.map +1 -0
  12. package/dist/decorators/delegate.decorator.js +108 -0
  13. package/dist/decorators/delegate.decorator.js.map +1 -0
  14. package/dist/executor/agent.executor.d.ts +1 -0
  15. package/dist/executor/agent.executor.d.ts.map +1 -1
  16. package/dist/executor/agent.executor.js +19 -10
  17. package/dist/executor/agent.executor.js.map +1 -1
  18. package/dist/executor/tool.executor.d.ts +3 -1
  19. package/dist/executor/tool.executor.d.ts.map +1 -1
  20. package/dist/executor/tool.executor.js +33 -2
  21. package/dist/executor/tool.executor.js.map +1 -1
  22. package/dist/graph/agent-graph.d.ts +131 -0
  23. package/dist/graph/agent-graph.d.ts.map +1 -0
  24. package/dist/graph/agent-graph.js +462 -0
  25. package/dist/graph/agent-graph.js.map +1 -0
  26. package/dist/graph/agent-graph.types.d.ts +210 -0
  27. package/dist/graph/agent-graph.types.d.ts.map +1 -0
  28. package/dist/graph/agent-graph.types.js +12 -0
  29. package/dist/graph/agent-graph.types.js.map +1 -0
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +5 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/prompts/agent-system.prompt.d.ts +10 -0
  35. package/dist/prompts/agent-system.prompt.d.ts.map +1 -0
  36. package/dist/prompts/agent-system.prompt.js +18 -0
  37. package/dist/prompts/agent-system.prompt.js.map +1 -0
  38. package/dist/prompts/index.d.ts +4 -0
  39. package/dist/prompts/index.d.ts.map +1 -0
  40. package/dist/prompts/index.js +20 -0
  41. package/dist/prompts/index.js.map +1 -0
  42. package/dist/prompts/supervisor-routing.prompt.d.ts +9 -0
  43. package/dist/prompts/supervisor-routing.prompt.d.ts.map +1 -0
  44. package/dist/prompts/supervisor-routing.prompt.js +22 -0
  45. package/dist/prompts/supervisor-routing.prompt.js.map +1 -0
  46. package/dist/prompts/supervisor-system.prompt.d.ts +9 -0
  47. package/dist/prompts/supervisor-system.prompt.d.ts.map +1 -0
  48. package/dist/prompts/supervisor-system.prompt.js +21 -0
  49. package/dist/prompts/supervisor-system.prompt.js.map +1 -0
  50. package/dist/runtime/agent.runtime.d.ts +65 -2
  51. package/dist/runtime/agent.runtime.d.ts.map +1 -1
  52. package/dist/runtime/agent.runtime.js +105 -4
  53. package/dist/runtime/agent.runtime.js.map +1 -1
  54. package/dist/supervisor/supervisor.d.ts +81 -0
  55. package/dist/supervisor/supervisor.d.ts.map +1 -0
  56. package/dist/supervisor/supervisor.js +220 -0
  57. package/dist/supervisor/supervisor.js.map +1 -0
  58. package/dist/types/agent.types.d.ts +15 -0
  59. package/dist/types/agent.types.d.ts.map +1 -1
  60. package/dist/types/agent.types.js.map +1 -1
  61. package/dist/types/event.types.d.ts +76 -1
  62. package/dist/types/event.types.d.ts.map +1 -1
  63. package/dist/types/event.types.js +16 -0
  64. package/dist/types/event.types.js.map +1 -1
  65. package/dist/utils/circuit-breaker.d.ts +5 -65
  66. package/dist/utils/circuit-breaker.d.ts.map +1 -1
  67. package/dist/utils/circuit-breaker.js +10 -150
  68. package/dist/utils/circuit-breaker.js.map +1 -1
  69. package/logs/combined.log +1 -1
  70. package/package.json +23 -8
  71. package/tsconfig.tsbuildinfo +1 -1
package/README.md CHANGED
@@ -1,17 +1,26 @@
1
1
  # @hazeljs/agent
2
2
 
3
- **AI-native Agent Runtime for HazelJS** - Build stateful, long-running agents with tools, memory, and human-in-the-loop workflows.
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)
4
10
 
5
11
  ## Overview
6
12
 
7
- The Agent Runtime is a core primitive in HazelJS designed for building production-grade AI agents. Unlike stateless request handlers, agents are:
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
8
22
 
9
- - **Stateful** - Maintain context across multiple steps
10
- - **Long-running** - Execute complex workflows over time
11
- - **Tool-using** - Call functions safely with approval workflows
12
- - **Memory-enabled** - Integrate with persistent memory systems
13
- - **Observable** - Full event system for monitoring and debugging
14
- - **Resumable** - Support pause/resume and human-in-the-loop
23
+ ---
15
24
 
16
25
  ## Installation
17
26
 
@@ -19,9 +28,11 @@ The Agent Runtime is a core primitive in HazelJS designed for building productio
19
28
  npm install @hazeljs/agent @hazeljs/core @hazeljs/rag
20
29
  ```
21
30
 
22
- ## Quick Start
31
+ ---
23
32
 
24
- ### 1. Define an Agent
33
+ ## Quick Start Single Agent
34
+
35
+ ### 1. Define an agent
25
36
 
26
37
  ```typescript
27
38
  import { Agent, Tool } from '@hazeljs/agent';
@@ -36,242 +47,375 @@ import { Agent, Tool } from '@hazeljs/agent';
36
47
  export class SupportAgent {
37
48
  @Tool({
38
49
  description: 'Look up order information by order ID',
39
- parameters: [
40
- {
41
- name: 'orderId',
42
- type: 'string',
43
- description: 'The order ID to lookup',
44
- required: true,
45
- },
46
- ],
50
+ parameters: [{ name: 'orderId', type: 'string', description: 'The order ID', required: true }],
47
51
  })
48
52
  async lookupOrder(input: { orderId: string }) {
49
- // Your implementation
50
- return {
51
- orderId: input.orderId,
52
- status: 'shipped',
53
- trackingNumber: 'TRACK123',
54
- };
53
+ return { orderId: input.orderId, status: 'shipped', trackingNumber: 'TRACK123' };
55
54
  }
56
55
 
57
56
  @Tool({
58
57
  description: 'Process a refund for an order',
59
- requiresApproval: true, // Requires human approval
58
+ requiresApproval: true, // requires human approval before execution
60
59
  parameters: [
61
- {
62
- name: 'orderId',
63
- type: 'string',
64
- description: 'The order ID to refund',
65
- required: true,
66
- },
67
- {
68
- name: 'amount',
69
- type: 'number',
70
- description: 'Refund amount',
71
- required: true,
72
- },
60
+ { name: 'orderId', type: 'string', required: true },
61
+ { name: 'amount', type: 'number', required: true },
73
62
  ],
74
63
  })
75
64
  async processRefund(input: { orderId: string; amount: number }) {
76
- // Your implementation
77
- return {
78
- success: true,
79
- refundId: 'REF123',
80
- amount: input.amount,
81
- };
65
+ return { success: true, refundId: 'REF123', amount: input.amount };
82
66
  }
83
67
  }
84
68
  ```
85
69
 
86
- ### 2. Set Up the Runtime
70
+ ### 2. Set up the runtime
87
71
 
88
72
  ```typescript
89
73
  import { AgentRuntime } from '@hazeljs/agent';
90
74
  import { MemoryManager } from '@hazeljs/rag';
91
75
  import { AIService } from '@hazeljs/ai';
92
76
 
93
- // Initialize dependencies
94
- const memoryManager = new MemoryManager(/* ... */);
95
- const aiService = new AIService({ provider: 'openai' });
96
-
97
- // Create runtime
98
77
  const runtime = new AgentRuntime({
99
- memoryManager,
100
- llmProvider: aiService,
78
+ memoryManager: new MemoryManager(/* ... */),
79
+ llmProvider: new AIService({ provider: 'openai' }),
101
80
  defaultMaxSteps: 10,
102
81
  enableObservability: true,
103
82
  });
104
83
 
105
- // Register agent
106
- const supportAgent = new SupportAgent();
84
+ const agent = new SupportAgent();
107
85
  runtime.registerAgent(SupportAgent);
108
- runtime.registerAgentInstance('support-agent', supportAgent);
86
+ runtime.registerAgentInstance('support-agent', agent);
109
87
  ```
110
88
 
111
- ### 3. Execute the Agent
89
+ ### 3. Execute
112
90
 
113
91
  ```typescript
114
- // Execute agent
115
92
  const result = await runtime.execute(
116
93
  'support-agent',
117
- 'I need to check my order status for order #12345',
118
- {
119
- sessionId: 'user-session-123',
120
- userId: 'user-456',
121
- enableMemory: true,
122
- enableRAG: true,
123
- }
94
+ 'I need to check my order #12345',
95
+ { sessionId: 'user-session-123', userId: 'user-456', enableMemory: true },
124
96
  );
125
97
 
126
98
  console.log(result.response);
127
99
  console.log(`Completed in ${result.steps.length} steps`);
128
100
  ```
129
101
 
130
- ### 4. Handle Human-in-the-Loop
102
+ ### 4. Handle human-in-the-loop
131
103
 
132
104
  ```typescript
133
- // Subscribe to approval requests
134
105
  runtime.on('tool.approval.requested', async (event) => {
135
106
  console.log('Approval needed:', event.data);
136
-
137
- // Approve or reject
138
107
  runtime.approveToolExecution(event.data.requestId, 'admin-user');
139
- // or
140
- // runtime.rejectToolExecution(event.data.requestId);
141
108
  });
142
109
 
143
- // Resume after approval
144
- const resumedResult = await runtime.resume(result.executionId);
110
+ const resumed = await runtime.resume(result.executionId);
145
111
  ```
146
112
 
147
- ## Core Concepts
113
+ ---
148
114
 
149
- ### Agent State Machine
115
+ ## Multi-Agent Orchestration
150
116
 
151
- Every agent execution follows a deterministic state machine:
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).
152
122
 
153
123
  ```
154
- idle → thinking → using_tool → thinking → ... → completed
155
-
156
- waiting_for_input
157
-
158
- waiting_for_approval
159
-
160
- failed
124
+ OrchestratorAgent
125
+ └── @Delegate → ResearchAgent
126
+ └── @Delegate → WriterAgent
161
127
  ```
162
128
 
163
- ### Execution Loop
129
+ ```typescript
130
+ import { Agent, Delegate } from '@hazeljs/agent';
164
131
 
165
- The agent runtime implements a controlled execution loop:
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
+ }
166
147
 
167
- 1. **Load State** - Restore agent context and memory
168
- 2. **Load Memory** - Retrieve conversation history
169
- 3. **Retrieve RAG** - Get relevant context (optional)
170
- 4. **Ask LLM** - Decide next action
171
- 5. **Execute Action** - Call tool, ask user, or respond
172
- 6. **Persist State** - Save state and memory
173
- 7. **Repeat or Finish** - Continue or complete
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
+ }
174
158
 
175
- ### Tools
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
+ }
176
166
 
177
- Tools are explicit, auditable capabilities:
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:**
178
177
 
179
178
  ```typescript
180
- @Tool({
181
- description: 'Send an email',
182
- requiresApproval: true,
183
- timeout: 30000,
184
- retries: 2,
185
- parameters: [
186
- { name: 'to', type: 'string', required: true },
187
- { name: 'subject', type: 'string', required: true },
188
- { name: 'body', type: 'string', required: true },
189
- ],
190
- })
191
- async sendEmail(input: { to: string; subject: string; body: string }) {
192
- // Implementation
193
- }
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);
194
189
  ```
195
190
 
196
- **Tool Features:**
197
- - Automatic parameter validation
198
- - Timeout and retry logic
199
- - Approval workflows
200
- - Execution logging
201
- - Error handling
191
+ > **Note:** `@Delegate` implicitly registers the method as `@Tool`. Do not add `@Tool` separately.
192
+
193
+ ---
202
194
 
203
- ### Memory Integration
195
+ ### Pattern 2 — `AgentGraph`: DAG pipelines
204
196
 
205
- Agents automatically integrate with HazelJS Memory:
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
+ ```
206
204
 
207
205
  ```typescript
208
- // Memory is automatically persisted
209
- const result = await runtime.execute('agent-name', 'Hello', {
210
- sessionId: 'session-123',
211
- enableMemory: true,
212
- });
206
+ import { END } from '@hazeljs/agent';
213
207
 
214
- // Conversation history is maintained
215
- const result2 = await runtime.execute('agent-name', 'What did I just say?', {
216
- sessionId: 'session-123', // Same session
217
- enableMemory: true,
218
- });
208
+ // Create graph via the runtime
209
+ const graph = runtime.createGraph('research-pipeline');
219
210
  ```
220
211
 
221
- ### RAG Integration
212
+ #### Sequential pipeline
222
213
 
223
- Agents can query RAG before reasoning:
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
224
229
 
225
230
  ```typescript
226
- @Agent({
227
- name: 'docs-agent',
228
- enableRAG: true,
229
- ragTopK: 5,
230
- })
231
- export class DocsAgent {
232
- // Agent automatically retrieves relevant docs
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 } };
233
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');
234
273
  ```
235
274
 
236
- ## Event System
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
+ ```
237
284
 
238
- Subscribe to agent events for observability:
285
+ #### `AgentGraph` API
239
286
 
240
287
  ```typescript
241
- import { AgentEventType } from '@hazeljs/agent';
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
+ }
242
295
 
243
- // Execution events
244
- runtime.on(AgentEventType.EXECUTION_STARTED, (event) => {
245
- console.log('Agent started:', event.data);
246
- });
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
+ ```
247
302
 
248
- runtime.on(AgentEventType.EXECUTION_COMPLETED, (event) => {
249
- console.log('Agent completed:', event.data);
250
- });
303
+ ---
251
304
 
252
- // Step events
253
- runtime.on(AgentEventType.STEP_STARTED, (event) => {
254
- console.log('Step started:', event.data);
255
- });
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.
256
308
 
257
- // Tool events
258
- runtime.on(AgentEventType.TOOL_EXECUTION_STARTED, (event) => {
259
- console.log('Tool executing:', event.data);
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
+ },
260
335
  });
261
336
 
262
- runtime.on(AgentEventType.TOOL_APPROVAL_REQUESTED, (event) => {
263
- console.log('Approval needed:', event.data);
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)}`);
264
345
  });
346
+ ```
347
+
348
+ **`SupervisorConfig`:**
265
349
 
266
- // Subscribe to all events
267
- runtime.onAny((event) => {
268
- console.log('Event:', event.type, event.data);
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');
269
410
  });
411
+
412
+ // Catch-all
413
+ runtime.onAny(e => console.log(e.type, e.data));
270
414
  ```
271
415
 
272
- ## HazelJS Module Integration
416
+ ---
273
417
 
274
- Use with HazelJS modules:
418
+ ## HazelJS Module Integration
275
419
 
276
420
  ```typescript
277
421
  import { HazelModule } from '@hazeljs/core';
@@ -282,146 +426,116 @@ import { RagModule } from '@hazeljs/rag';
282
426
  imports: [
283
427
  RagModule.forRoot({ /* ... */ }),
284
428
  AgentModule.forRoot({
285
- runtime: {
286
- defaultMaxSteps: 10,
287
- enableObservability: true,
288
- },
289
- agents: [SupportAgent, SalesAgent],
429
+ runtime: { defaultMaxSteps: 10, enableObservability: true },
430
+ agents: [SupportAgent, ResearchAgent, WriterAgent, OrchestratorAgent],
290
431
  }),
291
432
  ],
292
433
  })
293
434
  export class AppModule {}
294
435
  ```
295
436
 
296
- ## Advanced Usage
437
+ ---
297
438
 
298
- ### Pause and Resume
439
+ ## Best Practices
299
440
 
300
- ```typescript
301
- // Execute agent
302
- const result = await runtime.execute('agent', 'Start task');
441
+ ### Keep tools idempotent
303
442
 
304
- if (result.state === 'waiting_for_input') {
305
- // Agent is waiting for user input
306
- const resumed = await runtime.resume(result.executionId, 'User response');
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);
307
449
  }
308
450
  ```
309
451
 
310
- ### Custom Context
452
+ ### Use `@Delegate` for domain specialisation
311
453
 
312
- ```typescript
313
- const result = await runtime.execute('agent', 'Process order', {
314
- initialContext: {
315
- userId: '123',
316
- orderData: { /* ... */ },
317
- },
318
- });
319
- ```
454
+ Keep each agent focused on one domain. `@Delegate` lets the orchestrator combine specialists without any agent becoming a monolith.
320
455
 
321
- ### Tool Policies
456
+ ### Choose the right multi-agent pattern
322
457
 
323
- ```typescript
324
- @Tool({
325
- description: 'Delete user data',
326
- requiresApproval: true,
327
- policy: 'admin-only', // Custom policy
328
- })
329
- async deleteUserData(input: { userId: string }) {
330
- // Implementation
331
- }
332
- ```
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 |
333
463
 
334
- ## Architecture
464
+ ### Require approval for destructive actions
335
465
 
466
+ ```typescript
467
+ @Tool({ requiresApproval: true, description: 'Delete user account' })
468
+ async deleteAccount(input: { userId: string }) { /* ... */ }
336
469
  ```
337
- ┌─────────────────────────────────────────────────┐
338
- │ Agent Runtime │
339
- ├─────────────────────────────────────────────────┤
340
- │ ┌──────────────┐ ┌──────────────┐ │
341
- │ │ Registry │ │ State Mgr │ │
342
- │ └──────────────┘ └──────────────┘ │
343
- │ ┌──────────────┐ ┌──────────────┐ │
344
- │ │ Executor │ │ Tool Executor│ │
345
- │ └──────────────┘ └──────────────┘ │
346
- │ ┌──────────────┐ ┌──────────────┐ │
347
- │ │ Events │ │ Context │ │
348
- │ └──────────────┘ └──────────────┘ │
349
- ├─────────────────────────────────────────────────┤
350
- │ Memory Module │ RAG Module │
351
- └─────────────────────────────────────────────────┘
352
- ```
353
-
354
- ## Best Practices
355
470
 
356
- ### 1. Keep Agents Declarative
471
+ ### Handle errors in tools
357
472
 
358
473
  ```typescript
359
- // Good - Declarative
360
- @Agent({ name: 'support-agent' })
361
- export class SupportAgent {
362
- @Tool()
363
- async lookupOrder(input: { orderId: string }) {
364
- return this.orderService.find(input.orderId);
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 };
365
480
  }
366
481
  }
367
-
368
- // ❌ Bad - Business logic in decorator
369
- @Agent({
370
- name: 'support-agent',
371
- onExecute: async () => { /* complex logic */ }
372
- })
373
482
  ```
374
483
 
375
- ### 2. Use Approval for Destructive Actions
484
+ ---
376
485
 
377
- ```typescript
378
- @Tool({ requiresApproval: true })
379
- async deleteAccount(input: { userId: string }) {
380
- // Destructive action
381
- }
382
- ```
486
+ ## API Reference
383
487
 
384
- ### 3. Design Idempotent Tools
488
+ ### `AgentRuntime`
385
489
 
386
490
  ```typescript
387
- @Tool()
388
- async createOrder(input: { orderId: string; items: any[] }) {
389
- // Check if order exists first
390
- const existing = await this.findOrder(input.orderId);
391
- if (existing) return existing;
392
-
393
- return this.createNewOrder(input);
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;
394
502
  }
395
503
  ```
396
504
 
397
- ### 4. Handle Errors Gracefully
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
398
514
 
399
515
  ```typescript
400
- @Tool()
401
- async externalAPICall(input: any) {
402
- try {
403
- return await this.api.call(input);
404
- } catch (error) {
405
- // Return structured error
406
- return {
407
- success: false,
408
- error: error.message,
409
- };
410
- }
411
- }
412
- ```
516
+ // Agent node — runs a registered agent
517
+ { type: 'agent', agentName: string }
413
518
 
414
- ## API Reference
519
+ // Function node — runs a custom function
520
+ { type: 'function', fn: (state: GraphState) => Promise<GraphState> }
415
521
 
416
- See [API Documentation](./docs/api.md) for complete API reference.
522
+ // Parallel node fans out to multiple branches simultaneously
523
+ { type: 'parallel', branches: string[] }
524
+ ```
525
+
526
+ ---
417
527
 
418
528
  ## Examples
419
529
 
420
- - [Customer Support Agent](./examples/support-agent.ts)
421
- - [Sales Agent with Approval](./examples/sales-agent.ts)
422
- - [Multi-Agent System](./examples/multi-agent.ts)
423
- - [RAG-Powered Agent](./examples/rag-agent.ts)
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
+ ---
424
534
 
425
535
  ## License
426
536
 
427
- MIT
537
+ Apache 2.0
538
+
539
+ ## Contributing
540
+
541
+ Contributions are welcome! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for details.