@hazeljs/agent 0.2.0-beta.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.
- package/IMPLEMENTATION_SUMMARY.md +400 -0
- package/PERSISTENCE.md +201 -0
- package/PRISMA_INTEGRATION.md +499 -0
- package/PRODUCTION_READINESS.md +264 -0
- package/QUICKSTART.md +135 -0
- package/README.md +427 -0
- package/STATE_VS_MEMORY.md +243 -0
- package/dist/agent.module.d.ts +42 -0
- package/dist/agent.module.d.ts.map +1 -0
- package/dist/agent.module.js +90 -0
- package/dist/agent.module.js.map +1 -0
- package/dist/context/agent.context.d.ts +27 -0
- package/dist/context/agent.context.d.ts.map +1 -0
- package/dist/context/agent.context.js +98 -0
- package/dist/context/agent.context.js.map +1 -0
- package/dist/decorators/agent.decorator.d.ts +21 -0
- package/dist/decorators/agent.decorator.d.ts.map +1 -0
- package/dist/decorators/agent.decorator.js +38 -0
- package/dist/decorators/agent.decorator.js.map +1 -0
- package/dist/decorators/tool.decorator.d.ts +23 -0
- package/dist/decorators/tool.decorator.d.ts.map +1 -0
- package/dist/decorators/tool.decorator.js +61 -0
- package/dist/decorators/tool.decorator.js.map +1 -0
- package/dist/events/event.emitter.d.ts +45 -0
- package/dist/events/event.emitter.d.ts.map +1 -0
- package/dist/events/event.emitter.js +96 -0
- package/dist/events/event.emitter.js.map +1 -0
- package/dist/executor/agent.executor.d.ts +57 -0
- package/dist/executor/agent.executor.d.ts.map +1 -0
- package/dist/executor/agent.executor.js +303 -0
- package/dist/executor/agent.executor.js.map +1 -0
- package/dist/executor/tool.executor.d.ts +53 -0
- package/dist/executor/tool.executor.d.ts.map +1 -0
- package/dist/executor/tool.executor.js +234 -0
- package/dist/executor/tool.executor.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/agent.registry.d.ts +49 -0
- package/dist/registry/agent.registry.d.ts.map +1 -0
- package/dist/registry/agent.registry.js +90 -0
- package/dist/registry/agent.registry.js.map +1 -0
- package/dist/registry/tool.registry.d.ts +54 -0
- package/dist/registry/tool.registry.d.ts.map +1 -0
- package/dist/registry/tool.registry.js +153 -0
- package/dist/registry/tool.registry.js.map +1 -0
- package/dist/runtime/agent.runtime.d.ts +155 -0
- package/dist/runtime/agent.runtime.d.ts.map +1 -0
- package/dist/runtime/agent.runtime.extensions.d.ts +49 -0
- package/dist/runtime/agent.runtime.extensions.d.ts.map +1 -0
- package/dist/runtime/agent.runtime.extensions.js +93 -0
- package/dist/runtime/agent.runtime.extensions.js.map +1 -0
- package/dist/runtime/agent.runtime.js +347 -0
- package/dist/runtime/agent.runtime.js.map +1 -0
- package/dist/state/agent-state.interface.d.ts +63 -0
- package/dist/state/agent-state.interface.d.ts.map +1 -0
- package/dist/state/agent-state.interface.js +7 -0
- package/dist/state/agent-state.interface.js.map +1 -0
- package/dist/state/agent.state.d.ts +67 -0
- package/dist/state/agent.state.d.ts.map +1 -0
- package/dist/state/agent.state.js +172 -0
- package/dist/state/agent.state.js.map +1 -0
- package/dist/state/database-state.manager.d.ts +63 -0
- package/dist/state/database-state.manager.d.ts.map +1 -0
- package/dist/state/database-state.manager.js +282 -0
- package/dist/state/database-state.manager.js.map +1 -0
- package/dist/state/redis-state.manager.d.ts +81 -0
- package/dist/state/redis-state.manager.d.ts.map +1 -0
- package/dist/state/redis-state.manager.js +253 -0
- package/dist/state/redis-state.manager.js.map +1 -0
- package/dist/types/agent.types.d.ts +151 -0
- package/dist/types/agent.types.d.ts.map +1 -0
- package/dist/types/agent.types.js +32 -0
- package/dist/types/agent.types.js.map +1 -0
- package/dist/types/event.types.d.ts +123 -0
- package/dist/types/event.types.d.ts.map +1 -0
- package/dist/types/event.types.js +30 -0
- package/dist/types/event.types.js.map +1 -0
- package/dist/types/llm.types.d.ts +66 -0
- package/dist/types/llm.types.d.ts.map +1 -0
- package/dist/types/llm.types.js +7 -0
- package/dist/types/llm.types.js.map +1 -0
- package/dist/types/rag.types.d.ts +40 -0
- package/dist/types/rag.types.d.ts.map +1 -0
- package/dist/types/rag.types.js +7 -0
- package/dist/types/rag.types.js.map +1 -0
- package/dist/types/tool.types.d.ts +118 -0
- package/dist/types/tool.types.d.ts.map +1 -0
- package/dist/types/tool.types.js +19 -0
- package/dist/types/tool.types.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +69 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +156 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/health-check.d.ts +71 -0
- package/dist/utils/health-check.d.ts.map +1 -0
- package/dist/utils/health-check.js +156 -0
- package/dist/utils/health-check.js.map +1 -0
- package/dist/utils/logger.d.ts +53 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +133 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/metrics.d.ts +90 -0
- package/dist/utils/metrics.d.ts.map +1 -0
- package/dist/utils/metrics.js +186 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +44 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +82 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry.d.ts +42 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +103 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +58 -0
- package/prisma-schema.example.prisma +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# @hazeljs/agent
|
|
2
|
+
|
|
3
|
+
**AI-native Agent Runtime for HazelJS** - Build stateful, long-running agents with tools, memory, and human-in-the-loop workflows.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Agent Runtime is a core primitive in HazelJS designed for building production-grade AI agents. Unlike stateless request handlers, agents are:
|
|
8
|
+
|
|
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
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @hazeljs/agent @hazeljs/core @hazeljs/rag
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Define an Agent
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { Agent, Tool } from '@hazeljs/agent';
|
|
28
|
+
|
|
29
|
+
@Agent({
|
|
30
|
+
name: 'support-agent',
|
|
31
|
+
description: 'Customer support agent',
|
|
32
|
+
systemPrompt: 'You are a helpful customer support agent.',
|
|
33
|
+
enableMemory: true,
|
|
34
|
+
enableRAG: true,
|
|
35
|
+
})
|
|
36
|
+
export class SupportAgent {
|
|
37
|
+
@Tool({
|
|
38
|
+
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
|
+
],
|
|
47
|
+
})
|
|
48
|
+
async lookupOrder(input: { orderId: string }) {
|
|
49
|
+
// Your implementation
|
|
50
|
+
return {
|
|
51
|
+
orderId: input.orderId,
|
|
52
|
+
status: 'shipped',
|
|
53
|
+
trackingNumber: 'TRACK123',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Tool({
|
|
58
|
+
description: 'Process a refund for an order',
|
|
59
|
+
requiresApproval: true, // Requires human approval
|
|
60
|
+
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
|
+
},
|
|
73
|
+
],
|
|
74
|
+
})
|
|
75
|
+
async processRefund(input: { orderId: string; amount: number }) {
|
|
76
|
+
// Your implementation
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
refundId: 'REF123',
|
|
80
|
+
amount: input.amount,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. Set Up the Runtime
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { AgentRuntime } from '@hazeljs/agent';
|
|
90
|
+
import { MemoryManager } from '@hazeljs/rag';
|
|
91
|
+
import { AIService } from '@hazeljs/ai';
|
|
92
|
+
|
|
93
|
+
// Initialize dependencies
|
|
94
|
+
const memoryManager = new MemoryManager(/* ... */);
|
|
95
|
+
const aiService = new AIService({ provider: 'openai' });
|
|
96
|
+
|
|
97
|
+
// Create runtime
|
|
98
|
+
const runtime = new AgentRuntime({
|
|
99
|
+
memoryManager,
|
|
100
|
+
llmProvider: aiService,
|
|
101
|
+
defaultMaxSteps: 10,
|
|
102
|
+
enableObservability: true,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Register agent
|
|
106
|
+
const supportAgent = new SupportAgent();
|
|
107
|
+
runtime.registerAgent(SupportAgent);
|
|
108
|
+
runtime.registerAgentInstance('support-agent', supportAgent);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 3. Execute the Agent
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Execute agent
|
|
115
|
+
const result = await runtime.execute(
|
|
116
|
+
'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
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
console.log(result.response);
|
|
127
|
+
console.log(`Completed in ${result.steps.length} steps`);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 4. Handle Human-in-the-Loop
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Subscribe to approval requests
|
|
134
|
+
runtime.on('tool.approval.requested', async (event) => {
|
|
135
|
+
console.log('Approval needed:', event.data);
|
|
136
|
+
|
|
137
|
+
// Approve or reject
|
|
138
|
+
runtime.approveToolExecution(event.data.requestId, 'admin-user');
|
|
139
|
+
// or
|
|
140
|
+
// runtime.rejectToolExecution(event.data.requestId);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Resume after approval
|
|
144
|
+
const resumedResult = await runtime.resume(result.executionId);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Core Concepts
|
|
148
|
+
|
|
149
|
+
### Agent State Machine
|
|
150
|
+
|
|
151
|
+
Every agent execution follows a deterministic state machine:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
idle → thinking → using_tool → thinking → ... → completed
|
|
155
|
+
↓
|
|
156
|
+
waiting_for_input
|
|
157
|
+
↓
|
|
158
|
+
waiting_for_approval
|
|
159
|
+
↓
|
|
160
|
+
failed
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Execution Loop
|
|
164
|
+
|
|
165
|
+
The agent runtime implements a controlled execution loop:
|
|
166
|
+
|
|
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
|
|
174
|
+
|
|
175
|
+
### Tools
|
|
176
|
+
|
|
177
|
+
Tools are explicit, auditable capabilities:
|
|
178
|
+
|
|
179
|
+
```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
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Tool Features:**
|
|
197
|
+
- Automatic parameter validation
|
|
198
|
+
- Timeout and retry logic
|
|
199
|
+
- Approval workflows
|
|
200
|
+
- Execution logging
|
|
201
|
+
- Error handling
|
|
202
|
+
|
|
203
|
+
### Memory Integration
|
|
204
|
+
|
|
205
|
+
Agents automatically integrate with HazelJS Memory:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Memory is automatically persisted
|
|
209
|
+
const result = await runtime.execute('agent-name', 'Hello', {
|
|
210
|
+
sessionId: 'session-123',
|
|
211
|
+
enableMemory: true,
|
|
212
|
+
});
|
|
213
|
+
|
|
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
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### RAG Integration
|
|
222
|
+
|
|
223
|
+
Agents can query RAG before reasoning:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
@Agent({
|
|
227
|
+
name: 'docs-agent',
|
|
228
|
+
enableRAG: true,
|
|
229
|
+
ragTopK: 5,
|
|
230
|
+
})
|
|
231
|
+
export class DocsAgent {
|
|
232
|
+
// Agent automatically retrieves relevant docs
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Event System
|
|
237
|
+
|
|
238
|
+
Subscribe to agent events for observability:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { AgentEventType } from '@hazeljs/agent';
|
|
242
|
+
|
|
243
|
+
// Execution events
|
|
244
|
+
runtime.on(AgentEventType.EXECUTION_STARTED, (event) => {
|
|
245
|
+
console.log('Agent started:', event.data);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
runtime.on(AgentEventType.EXECUTION_COMPLETED, (event) => {
|
|
249
|
+
console.log('Agent completed:', event.data);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Step events
|
|
253
|
+
runtime.on(AgentEventType.STEP_STARTED, (event) => {
|
|
254
|
+
console.log('Step started:', event.data);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Tool events
|
|
258
|
+
runtime.on(AgentEventType.TOOL_EXECUTION_STARTED, (event) => {
|
|
259
|
+
console.log('Tool executing:', event.data);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
runtime.on(AgentEventType.TOOL_APPROVAL_REQUESTED, (event) => {
|
|
263
|
+
console.log('Approval needed:', event.data);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Subscribe to all events
|
|
267
|
+
runtime.onAny((event) => {
|
|
268
|
+
console.log('Event:', event.type, event.data);
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## HazelJS Module Integration
|
|
273
|
+
|
|
274
|
+
Use with HazelJS modules:
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { HazelModule } from '@hazeljs/core';
|
|
278
|
+
import { AgentModule } from '@hazeljs/agent';
|
|
279
|
+
import { RagModule } from '@hazeljs/rag';
|
|
280
|
+
|
|
281
|
+
@HazelModule({
|
|
282
|
+
imports: [
|
|
283
|
+
RagModule.forRoot({ /* ... */ }),
|
|
284
|
+
AgentModule.forRoot({
|
|
285
|
+
runtime: {
|
|
286
|
+
defaultMaxSteps: 10,
|
|
287
|
+
enableObservability: true,
|
|
288
|
+
},
|
|
289
|
+
agents: [SupportAgent, SalesAgent],
|
|
290
|
+
}),
|
|
291
|
+
],
|
|
292
|
+
})
|
|
293
|
+
export class AppModule {}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Advanced Usage
|
|
297
|
+
|
|
298
|
+
### Pause and Resume
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// Execute agent
|
|
302
|
+
const result = await runtime.execute('agent', 'Start task');
|
|
303
|
+
|
|
304
|
+
if (result.state === 'waiting_for_input') {
|
|
305
|
+
// Agent is waiting for user input
|
|
306
|
+
const resumed = await runtime.resume(result.executionId, 'User response');
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Custom Context
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
const result = await runtime.execute('agent', 'Process order', {
|
|
314
|
+
initialContext: {
|
|
315
|
+
userId: '123',
|
|
316
|
+
orderData: { /* ... */ },
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Tool Policies
|
|
322
|
+
|
|
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
|
+
```
|
|
333
|
+
|
|
334
|
+
## Architecture
|
|
335
|
+
|
|
336
|
+
```
|
|
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
|
+
|
|
356
|
+
### 1. Keep Agents Declarative
|
|
357
|
+
|
|
358
|
+
```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);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ❌ Bad - Business logic in decorator
|
|
369
|
+
@Agent({
|
|
370
|
+
name: 'support-agent',
|
|
371
|
+
onExecute: async () => { /* complex logic */ }
|
|
372
|
+
})
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### 2. Use Approval for Destructive Actions
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
@Tool({ requiresApproval: true })
|
|
379
|
+
async deleteAccount(input: { userId: string }) {
|
|
380
|
+
// Destructive action
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### 3. Design Idempotent Tools
|
|
385
|
+
|
|
386
|
+
```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);
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### 4. Handle Errors Gracefully
|
|
398
|
+
|
|
399
|
+
```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
|
+
```
|
|
413
|
+
|
|
414
|
+
## API Reference
|
|
415
|
+
|
|
416
|
+
See [API Documentation](./docs/api.md) for complete API reference.
|
|
417
|
+
|
|
418
|
+
## Examples
|
|
419
|
+
|
|
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)
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
MIT
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Agent State vs Memory: Understanding the Difference
|
|
2
|
+
|
|
3
|
+
There are **two separate persistence layers** in the agent system that serve different purposes:
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────────────┐
|
|
9
|
+
│ Agent Execution │
|
|
10
|
+
└─────────────────────────────────────────────────────────┘
|
|
11
|
+
│ │
|
|
12
|
+
▼ ▼
|
|
13
|
+
┌──────────────────────┐ ┌──────────────────────────────┐
|
|
14
|
+
│ Agent State │ │ Memory (RAG) │
|
|
15
|
+
│ Persistence │ │ Persistence │
|
|
16
|
+
│ │ │ │
|
|
17
|
+
│ - Execution flow │ │ - Conversation history │
|
|
18
|
+
│ - Steps │ │ - Entities │
|
|
19
|
+
│ - State transitions │ │ - Facts │
|
|
20
|
+
│ - Execution context │ │ - Working memory │
|
|
21
|
+
│ - Metadata │ │ - Long-term knowledge │
|
|
22
|
+
└──────────────────────┘ └──────────────────────────────┘
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Agent State Persistence
|
|
26
|
+
|
|
27
|
+
**Purpose**: Track the **execution flow** and **state machine** of agent runs
|
|
28
|
+
|
|
29
|
+
**What it stores**:
|
|
30
|
+
- ✅ Execution ID and context
|
|
31
|
+
- ✅ Current state (IDLE, THINKING, WAITING_FOR_APPROVAL, etc.)
|
|
32
|
+
- ✅ Execution steps (what the agent did)
|
|
33
|
+
- ✅ Step results and errors
|
|
34
|
+
- ✅ Execution metadata
|
|
35
|
+
- ✅ **Temporary** conversation history (during execution)
|
|
36
|
+
|
|
37
|
+
**Lifetime**:
|
|
38
|
+
- **Short-lived** - typically minutes to hours
|
|
39
|
+
- Ephemeral - deleted after execution completes (or TTL expires)
|
|
40
|
+
|
|
41
|
+
**Use cases**:
|
|
42
|
+
- Resume paused executions
|
|
43
|
+
- Track execution progress
|
|
44
|
+
- Debug failed executions
|
|
45
|
+
- Monitor active agent runs
|
|
46
|
+
- State machine transitions
|
|
47
|
+
|
|
48
|
+
**Backends**:
|
|
49
|
+
- In-Memory (default)
|
|
50
|
+
- Redis (production)
|
|
51
|
+
- Database/Prisma (audit)
|
|
52
|
+
|
|
53
|
+
**Example**:
|
|
54
|
+
```typescript
|
|
55
|
+
// Agent State tracks: "Agent executed step 1, 2, 3, now waiting for approval"
|
|
56
|
+
{
|
|
57
|
+
executionId: "abc-123",
|
|
58
|
+
state: "WAITING_FOR_APPROVAL",
|
|
59
|
+
steps: [
|
|
60
|
+
{ stepNumber: 1, action: "think", result: {...} },
|
|
61
|
+
{ stepNumber: 2, action: "use_tool", result: {...} },
|
|
62
|
+
{ stepNumber: 3, action: "ask_user", result: {...} }
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Memory Persistence (RAG MemoryManager)
|
|
68
|
+
|
|
69
|
+
**Purpose**: Store **long-term knowledge** and **context** across sessions
|
|
70
|
+
|
|
71
|
+
**What it stores**:
|
|
72
|
+
- ✅ Conversation history (across all sessions)
|
|
73
|
+
- ✅ Entities (people, places, things mentioned)
|
|
74
|
+
- ✅ Facts (learned information)
|
|
75
|
+
- ✅ Working memory (user preferences, session state)
|
|
76
|
+
- ✅ Events (important occurrences)
|
|
77
|
+
|
|
78
|
+
**Lifetime**:
|
|
79
|
+
- **Long-lived** - days, weeks, months
|
|
80
|
+
- Persistent - survives agent restarts
|
|
81
|
+
- Cross-session - shared across multiple agent runs
|
|
82
|
+
|
|
83
|
+
**Use cases**:
|
|
84
|
+
- Build context for new conversations
|
|
85
|
+
- Remember entities across sessions
|
|
86
|
+
- Store learned facts
|
|
87
|
+
- Maintain user preferences
|
|
88
|
+
- Semantic search of past conversations
|
|
89
|
+
|
|
90
|
+
**Backends**:
|
|
91
|
+
- BufferMemory (in-memory)
|
|
92
|
+
- VectorMemory (Pinecone, Weaviate, Qdrant, ChromaDB)
|
|
93
|
+
- HybridMemory (combination)
|
|
94
|
+
|
|
95
|
+
**Example**:
|
|
96
|
+
```typescript
|
|
97
|
+
// Memory stores: "User's name is John, likes coffee, mentioned Paris last week"
|
|
98
|
+
{
|
|
99
|
+
entities: [
|
|
100
|
+
{ name: "John", type: "person", attributes: {...} },
|
|
101
|
+
{ name: "Paris", type: "location", attributes: {...} }
|
|
102
|
+
],
|
|
103
|
+
facts: ["User prefers coffee over tea"],
|
|
104
|
+
workingMemory: { "user_preferences": { "drink": "coffee" } }
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Key Differences
|
|
109
|
+
|
|
110
|
+
| Aspect | Agent State | Memory (RAG) |
|
|
111
|
+
|--------|-------------|--------------|
|
|
112
|
+
| **Purpose** | Execution flow tracking | Long-term knowledge |
|
|
113
|
+
| **Lifetime** | Minutes to hours | Days to months |
|
|
114
|
+
| **Scope** | Single execution | Cross-session |
|
|
115
|
+
| **Data** | Steps, state, metadata | Conversations, entities, facts |
|
|
116
|
+
| **Query** | By executionId | Semantic search, by sessionId |
|
|
117
|
+
| **Backend** | Redis/DB | Vector stores (Pinecone, etc.) |
|
|
118
|
+
| **When used** | During execution | Before/after execution |
|
|
119
|
+
|
|
120
|
+
## How They Work Together
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// 1. Agent starts execution
|
|
124
|
+
const context = stateManager.createContext(...);
|
|
125
|
+
|
|
126
|
+
// 2. Load memory (conversation history, entities) into context
|
|
127
|
+
await contextBuilder.buildWithMemory(context);
|
|
128
|
+
// ↑ This reads from Memory (RAG), populates context.memory
|
|
129
|
+
|
|
130
|
+
// 3. Agent executes (state tracked in Agent State)
|
|
131
|
+
await runtime.execute(...);
|
|
132
|
+
// ↑ State manager tracks steps, state transitions
|
|
133
|
+
|
|
134
|
+
// 4. After execution, persist to Memory
|
|
135
|
+
await contextBuilder.persistToMemory(context);
|
|
136
|
+
// ↑ This writes conversation history, entities to Memory (RAG)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Data Flow
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
┌─────────────────────────────────────────────────────────┐
|
|
143
|
+
│ Execution Start │
|
|
144
|
+
└─────────────────────────────────────────────────────────┘
|
|
145
|
+
│
|
|
146
|
+
▼
|
|
147
|
+
┌──────────────────────┐
|
|
148
|
+
│ Agent State │ ← Create execution context
|
|
149
|
+
│ (Redis/DB) │
|
|
150
|
+
└──────────────────────┘
|
|
151
|
+
│
|
|
152
|
+
▼
|
|
153
|
+
┌──────────────────────┐
|
|
154
|
+
│ Memory (RAG) │ ← Load conversation history, entities
|
|
155
|
+
│ (Vector Store) │ into execution context
|
|
156
|
+
└──────────────────────┘
|
|
157
|
+
│
|
|
158
|
+
▼
|
|
159
|
+
┌──────────────────────┐
|
|
160
|
+
│ Agent Executes │ ← State manager tracks steps
|
|
161
|
+
│ │ Memory provides context
|
|
162
|
+
└──────────────────────┘
|
|
163
|
+
│
|
|
164
|
+
▼
|
|
165
|
+
┌──────────────────────┐
|
|
166
|
+
│ Memory (RAG) │ ← Persist conversation, entities
|
|
167
|
+
│ (Vector Store) │ for future sessions
|
|
168
|
+
└──────────────────────┘
|
|
169
|
+
│
|
|
170
|
+
▼
|
|
171
|
+
┌──────────────────────┐
|
|
172
|
+
│ Agent State │ ← Archive execution (optional)
|
|
173
|
+
│ (Redis/DB) │
|
|
174
|
+
└──────────────────────┘
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Overlap: Conversation History
|
|
178
|
+
|
|
179
|
+
**Both** store conversation history, but for different reasons:
|
|
180
|
+
|
|
181
|
+
### Agent State Conversation History
|
|
182
|
+
- **Purpose**: Track messages **during** execution
|
|
183
|
+
- **Scope**: Single execution only
|
|
184
|
+
- **Lifetime**: Until execution completes
|
|
185
|
+
- **Use**: Resume paused executions, debug current run
|
|
186
|
+
|
|
187
|
+
### Memory Conversation History
|
|
188
|
+
- **Purpose**: Build context for **future** conversations
|
|
189
|
+
- **Scope**: All sessions for a user/session
|
|
190
|
+
- **Lifetime**: Long-term (weeks/months)
|
|
191
|
+
- **Use**: Semantic search, context building, continuity
|
|
192
|
+
|
|
193
|
+
## When to Use Which
|
|
194
|
+
|
|
195
|
+
### Use Agent State Persistence when:
|
|
196
|
+
- ✅ You need to resume paused executions
|
|
197
|
+
- ✅ You want to track execution progress
|
|
198
|
+
- ✅ You need to debug failed runs
|
|
199
|
+
- ✅ You want to monitor active agents
|
|
200
|
+
- ✅ You need execution audit trails
|
|
201
|
+
|
|
202
|
+
### Use Memory Persistence when:
|
|
203
|
+
- ✅ You want agents to remember past conversations
|
|
204
|
+
- ✅ You need entity tracking across sessions
|
|
205
|
+
- ✅ You want to store learned facts
|
|
206
|
+
- ✅ You need semantic search of conversations
|
|
207
|
+
- ✅ You want to maintain user preferences
|
|
208
|
+
|
|
209
|
+
## Recommended Setup
|
|
210
|
+
|
|
211
|
+
### Development
|
|
212
|
+
```typescript
|
|
213
|
+
// In-memory for both (default)
|
|
214
|
+
const runtime = new AgentRuntime({
|
|
215
|
+
// stateManager: default (in-memory)
|
|
216
|
+
// memoryManager: default (BufferMemory)
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Production
|
|
221
|
+
```typescript
|
|
222
|
+
// Redis for agent state (fast, distributed)
|
|
223
|
+
// Vector store (Pinecone) for memory (semantic search)
|
|
224
|
+
const stateManager = new RedisStateManager({ client: redisClient });
|
|
225
|
+
const memoryStore = new VectorMemory(pineconeStore, embeddings);
|
|
226
|
+
const memoryManager = new MemoryManager(memoryStore);
|
|
227
|
+
|
|
228
|
+
const runtime = new AgentRuntime({
|
|
229
|
+
stateManager, // ← Agent execution state
|
|
230
|
+
memoryManager, // ← Long-term memory
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Summary
|
|
235
|
+
|
|
236
|
+
- **Agent State** = "What is the agent doing right now?"
|
|
237
|
+
- **Memory** = "What does the agent know from past conversations?"
|
|
238
|
+
|
|
239
|
+
They complement each other:
|
|
240
|
+
- **State** enables resumable, trackable executions
|
|
241
|
+
- **Memory** enables context-aware, continuous conversations
|
|
242
|
+
|
|
243
|
+
Both are needed for a production-ready agent system!
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Module
|
|
3
|
+
* HazelJS module for Agent Runtime
|
|
4
|
+
*/
|
|
5
|
+
import { AgentRuntime, AgentRuntimeConfig } from './runtime/agent.runtime';
|
|
6
|
+
import { AgentEventType } from './types/event.types';
|
|
7
|
+
type NewableFunction = new (...args: unknown[]) => unknown;
|
|
8
|
+
/**
|
|
9
|
+
* Agent Module Options
|
|
10
|
+
*/
|
|
11
|
+
export interface AgentModuleOptions {
|
|
12
|
+
runtime?: AgentRuntimeConfig;
|
|
13
|
+
agents?: NewableFunction[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Agent Service
|
|
17
|
+
* Injectable service for agent runtime
|
|
18
|
+
*/
|
|
19
|
+
export declare class AgentService {
|
|
20
|
+
private runtime;
|
|
21
|
+
constructor(config?: AgentRuntimeConfig);
|
|
22
|
+
getRuntime(): AgentRuntime;
|
|
23
|
+
execute(agentName: string, input: string, options?: Record<string, unknown>): Promise<unknown>;
|
|
24
|
+
resume(executionId: string, input?: string): Promise<unknown>;
|
|
25
|
+
getContext(executionId: string): unknown;
|
|
26
|
+
on(type: AgentEventType, handler: (event: unknown) => void): void;
|
|
27
|
+
getAgents(): unknown[];
|
|
28
|
+
approveToolExecution(requestId: string, approvedBy: string): void;
|
|
29
|
+
rejectToolExecution(requestId: string): void;
|
|
30
|
+
getPendingApprovals(): unknown[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Agent Module
|
|
34
|
+
* Uses static configuration pattern compatible with HazelJS DI
|
|
35
|
+
*/
|
|
36
|
+
export declare class AgentModule {
|
|
37
|
+
private static options;
|
|
38
|
+
static forRoot(options?: AgentModuleOptions): typeof AgentModule;
|
|
39
|
+
static getOptions(): AgentModuleOptions;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=agent.module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.module.d.ts","sourceRoot":"","sources":["../src/agent.module.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,KAAK,eAAe,GAAG,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED;;;GAGG;AACH,qBACa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAe;gBAElB,MAAM,GAAE,kBAAuB;IAa3C,UAAU,IAAI,YAAY;IAIpB,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,OAAO,CAAC;IAIb,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAInE,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIxC,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAIjE,SAAS,IAAI,OAAO,EAAE;IAItB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAIjE,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI5C,mBAAmB,IAAI,OAAO,EAAE;CAGjC;AAED;;;GAGG;AACH,qBAIa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,OAAO,CAA0B;IAEhD,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,WAAW;IAKpE,MAAM,CAAC,UAAU,IAAI,kBAAkB;CAGxC"}
|