@falai/agent 0.3.12 → 0.3.21

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 (69) hide show
  1. package/README.md +74 -0
  2. package/dist/adapters/MemoryAdapter.d.ts +47 -0
  3. package/dist/adapters/MemoryAdapter.d.ts.map +1 -0
  4. package/dist/adapters/MemoryAdapter.js +178 -0
  5. package/dist/adapters/MemoryAdapter.js.map +1 -0
  6. package/dist/adapters/MongoAdapter.d.ts +97 -0
  7. package/dist/adapters/MongoAdapter.d.ts.map +1 -0
  8. package/dist/adapters/MongoAdapter.js +163 -0
  9. package/dist/adapters/MongoAdapter.js.map +1 -0
  10. package/dist/adapters/PostgreSQLAdapter.d.ts +71 -0
  11. package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -0
  12. package/dist/adapters/PostgreSQLAdapter.js +256 -0
  13. package/dist/adapters/PostgreSQLAdapter.js.map +1 -0
  14. package/dist/adapters/RedisAdapter.d.ts +71 -0
  15. package/dist/adapters/RedisAdapter.d.ts.map +1 -0
  16. package/dist/adapters/RedisAdapter.js +226 -0
  17. package/dist/adapters/RedisAdapter.js.map +1 -0
  18. package/dist/adapters/SQLiteAdapter.d.ts +69 -0
  19. package/dist/adapters/SQLiteAdapter.d.ts.map +1 -0
  20. package/dist/adapters/SQLiteAdapter.js +307 -0
  21. package/dist/adapters/SQLiteAdapter.js.map +1 -0
  22. package/dist/adapters/index.d.ts +9 -0
  23. package/dist/adapters/index.d.ts.map +1 -1
  24. package/dist/adapters/index.js +5 -0
  25. package/dist/adapters/index.js.map +1 -1
  26. package/dist/cjs/adapters/MemoryAdapter.d.ts +47 -0
  27. package/dist/cjs/adapters/MemoryAdapter.d.ts.map +1 -0
  28. package/dist/cjs/adapters/MemoryAdapter.js +182 -0
  29. package/dist/cjs/adapters/MemoryAdapter.js.map +1 -0
  30. package/dist/cjs/adapters/MongoAdapter.d.ts +97 -0
  31. package/dist/cjs/adapters/MongoAdapter.d.ts.map +1 -0
  32. package/dist/cjs/adapters/MongoAdapter.js +167 -0
  33. package/dist/cjs/adapters/MongoAdapter.js.map +1 -0
  34. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +71 -0
  35. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +1 -0
  36. package/dist/cjs/adapters/PostgreSQLAdapter.js +260 -0
  37. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -0
  38. package/dist/cjs/adapters/RedisAdapter.d.ts +71 -0
  39. package/dist/cjs/adapters/RedisAdapter.d.ts.map +1 -0
  40. package/dist/cjs/adapters/RedisAdapter.js +230 -0
  41. package/dist/cjs/adapters/RedisAdapter.js.map +1 -0
  42. package/dist/cjs/adapters/SQLiteAdapter.d.ts +69 -0
  43. package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +1 -0
  44. package/dist/cjs/adapters/SQLiteAdapter.js +311 -0
  45. package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -0
  46. package/dist/cjs/adapters/index.d.ts +9 -0
  47. package/dist/cjs/adapters/index.d.ts.map +1 -1
  48. package/dist/cjs/adapters/index.js +11 -1
  49. package/dist/cjs/adapters/index.js.map +1 -1
  50. package/dist/cjs/index.d.ts +9 -0
  51. package/dist/cjs/index.d.ts.map +1 -1
  52. package/dist/cjs/index.js +11 -1
  53. package/dist/cjs/index.js.map +1 -1
  54. package/dist/index.d.ts +9 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +5 -0
  57. package/dist/index.js.map +1 -1
  58. package/docs/ADAPTERS.md +151 -0
  59. package/docs/API_REFERENCE.md +448 -0
  60. package/docs/PERSISTENCE.md +176 -6
  61. package/examples/redis-persistence.ts +89 -0
  62. package/package.json +26 -2
  63. package/src/adapters/MemoryAdapter.ts +245 -0
  64. package/src/adapters/MongoAdapter.ts +295 -0
  65. package/src/adapters/PostgreSQLAdapter.ts +417 -0
  66. package/src/adapters/RedisAdapter.ts +365 -0
  67. package/src/adapters/SQLiteAdapter.ts +449 -0
  68. package/src/adapters/index.ts +27 -0
  69. package/src/index.ts +22 -0
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Redis adapter for persistence
3
+ * Uses Redis for fast session/message storage
4
+ */
5
+
6
+ import type {
7
+ SessionRepository,
8
+ MessageRepository,
9
+ SessionData,
10
+ MessageData,
11
+ SessionStatus,
12
+ PersistenceAdapter,
13
+ } from "../types/persistence";
14
+
15
+ /**
16
+ * Redis client interface - matches ioredis/redis clients
17
+ */
18
+ export interface RedisClient {
19
+ get(key: string): Promise<string | null>;
20
+ set(key: string, value: string): Promise<string | null>;
21
+ setex(key: string, seconds: number, value: string): Promise<string>;
22
+ del(...keys: string[]): Promise<number>;
23
+ keys(pattern: string): Promise<string[]>;
24
+ hgetall(key: string): Promise<Record<string, string>>;
25
+ hset(key: string, field: string, value: string): Promise<number>;
26
+ expire(key: string, seconds: number): Promise<number>;
27
+ quit(): Promise<string>;
28
+ }
29
+
30
+ /**
31
+ * Options for Redis adapter
32
+ */
33
+ export interface RedisAdapterOptions {
34
+ /**
35
+ * Redis client instance (ioredis or node-redis)
36
+ */
37
+ redis: RedisClient;
38
+
39
+ /**
40
+ * Key prefix for all keys (default: "agent:")
41
+ */
42
+ keyPrefix?: string;
43
+
44
+ /**
45
+ * TTL in seconds for sessions (default: 7 days)
46
+ */
47
+ sessionTTL?: number;
48
+
49
+ /**
50
+ * TTL in seconds for messages (default: 30 days)
51
+ */
52
+ messageTTL?: number;
53
+ }
54
+
55
+ /**
56
+ * Redis Adapter - Provider-style API for Redis persistence
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * import Redis from 'ioredis';
61
+ * import { Agent, RedisAdapter } from '@falai/agent';
62
+ *
63
+ * const redis = new Redis();
64
+ *
65
+ * const agent = new Agent({
66
+ * name: "My Agent",
67
+ * ai: provider,
68
+ * persistence: {
69
+ * adapter: new RedisAdapter({ redis }),
70
+ * userId: "user_123",
71
+ * },
72
+ * });
73
+ * ```
74
+ */
75
+ export class RedisAdapter implements PersistenceAdapter {
76
+ public readonly sessionRepository: SessionRepository;
77
+ public readonly messageRepository: MessageRepository;
78
+ private redis: RedisClient;
79
+ private keyPrefix: string;
80
+ private sessionTTL: number;
81
+ private messageTTL: number;
82
+
83
+ constructor(options: RedisAdapterOptions) {
84
+ this.redis = options.redis;
85
+ this.keyPrefix = options.keyPrefix || "agent:";
86
+ this.sessionTTL = options.sessionTTL || 7 * 24 * 60 * 60; // 7 days
87
+ this.messageTTL = options.messageTTL || 30 * 24 * 60 * 60; // 30 days
88
+
89
+ this.sessionRepository = new RedisSessionRepository(
90
+ this.redis,
91
+ this.keyPrefix,
92
+ this.sessionTTL
93
+ );
94
+
95
+ this.messageRepository = new RedisMessageRepository(
96
+ this.redis,
97
+ this.keyPrefix,
98
+ this.messageTTL
99
+ );
100
+ }
101
+
102
+ async disconnect(): Promise<void> {
103
+ await this.redis.quit();
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Redis Session Repository
109
+ */
110
+ class RedisSessionRepository implements SessionRepository {
111
+ constructor(
112
+ private redis: RedisClient,
113
+ private keyPrefix: string,
114
+ private ttl: number
115
+ ) {}
116
+
117
+ private getKey(id: string): string {
118
+ return `${this.keyPrefix}session:${id}`;
119
+ }
120
+
121
+ private getUserKey(userId: string): string {
122
+ return `${this.keyPrefix}user:${userId}:sessions`;
123
+ }
124
+
125
+ async create(
126
+ data: Omit<SessionData, "id" | "createdAt" | "updatedAt">
127
+ ): Promise<SessionData> {
128
+ const id = `session_${Date.now()}_${Math.random().toString(36).slice(2)}`;
129
+ const now = new Date();
130
+ const session: SessionData = {
131
+ ...data,
132
+ id,
133
+ createdAt: now,
134
+ updatedAt: now,
135
+ status: data.status || "active",
136
+ messageCount: data.messageCount || 0,
137
+ };
138
+
139
+ await this.redis.setex(this.getKey(id), this.ttl, JSON.stringify(session));
140
+
141
+ // Add to user's session list
142
+ if (data.userId) {
143
+ await this.redis.hset(
144
+ this.getUserKey(data.userId),
145
+ id,
146
+ now.toISOString()
147
+ );
148
+ }
149
+
150
+ return session;
151
+ }
152
+
153
+ async findById(id: string): Promise<SessionData | null> {
154
+ const data = await this.redis.get(this.getKey(id));
155
+ if (!data) return null;
156
+ try {
157
+ return JSON.parse(data) as SessionData;
158
+ } catch (error) {
159
+ console.error(`Error parsing session data for id ${id}:`, error);
160
+ return null;
161
+ }
162
+ }
163
+
164
+ async findActiveByUserId(userId: string): Promise<SessionData | null> {
165
+ const sessionIds = await this.redis.hgetall(this.getUserKey(userId));
166
+
167
+ for (const sessionId of Object.keys(sessionIds)) {
168
+ const session = await this.findById(sessionId);
169
+ if (session && session.status === "active") {
170
+ return session;
171
+ }
172
+ }
173
+
174
+ return null;
175
+ }
176
+
177
+ async findByUserId(userId: string, limit = 100): Promise<SessionData[]> {
178
+ const sessionIds = await this.redis.hgetall(this.getUserKey(userId));
179
+ const sessions: SessionData[] = [];
180
+
181
+ for (const sessionId of Object.keys(sessionIds).slice(0, limit)) {
182
+ const session = await this.findById(sessionId);
183
+ if (session) {
184
+ sessions.push(session);
185
+ }
186
+ }
187
+
188
+ return sessions.sort(
189
+ (a, b) => b.createdAt.getTime() - a.createdAt.getTime()
190
+ );
191
+ }
192
+
193
+ async update(
194
+ id: string,
195
+ data: Partial<Omit<SessionData, "id" | "createdAt">>
196
+ ): Promise<SessionData | null> {
197
+ const existing = await this.findById(id);
198
+ if (!existing) return null;
199
+
200
+ const updated: SessionData = {
201
+ ...existing,
202
+ ...data,
203
+ updatedAt: new Date(),
204
+ };
205
+
206
+ await this.redis.setex(this.getKey(id), this.ttl, JSON.stringify(updated));
207
+
208
+ return updated;
209
+ }
210
+
211
+ async updateStatus(
212
+ id: string,
213
+ status: SessionStatus,
214
+ completedAt?: Date
215
+ ): Promise<SessionData | null> {
216
+ return this.update(id, { status, completedAt });
217
+ }
218
+
219
+ async updateCollectedData(
220
+ id: string,
221
+ collectedData: Record<string, unknown>
222
+ ): Promise<SessionData | null> {
223
+ return this.update(id, { collectedData });
224
+ }
225
+
226
+ async updateRouteState(
227
+ id: string,
228
+ route?: string,
229
+ state?: string
230
+ ): Promise<SessionData | null> {
231
+ return this.update(id, { currentRoute: route, currentState: state });
232
+ }
233
+
234
+ async incrementMessageCount(id: string): Promise<SessionData | null> {
235
+ const session = await this.findById(id);
236
+ if (!session) return null;
237
+
238
+ return this.update(id, {
239
+ messageCount: (session.messageCount || 0) + 1,
240
+ lastMessageAt: new Date(),
241
+ });
242
+ }
243
+
244
+ async delete(id: string): Promise<boolean> {
245
+ const result = await this.redis.del(this.getKey(id));
246
+ return result > 0;
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Redis Message Repository
252
+ */
253
+ class RedisMessageRepository implements MessageRepository {
254
+ constructor(
255
+ private redis: RedisClient,
256
+ private keyPrefix: string,
257
+ private ttl: number
258
+ ) {}
259
+
260
+ private getKey(id: string): string {
261
+ return `${this.keyPrefix}message:${id}`;
262
+ }
263
+
264
+ private getSessionKey(sessionId: string): string {
265
+ return `${this.keyPrefix}session:${sessionId}:messages`;
266
+ }
267
+
268
+ async create(
269
+ data: Omit<MessageData, "id" | "createdAt">
270
+ ): Promise<MessageData> {
271
+ const id = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
272
+ const message: MessageData = {
273
+ ...data,
274
+ id,
275
+ createdAt: new Date(),
276
+ };
277
+
278
+ await this.redis.setex(this.getKey(id), this.ttl, JSON.stringify(message));
279
+ await this.redis.hset(
280
+ this.getSessionKey(data.sessionId),
281
+ id,
282
+ message.createdAt.toISOString()
283
+ );
284
+
285
+ return message;
286
+ }
287
+
288
+ async findById(id: string): Promise<MessageData | null> {
289
+ const data = await this.redis.get(this.getKey(id));
290
+ if (!data) return null;
291
+ try {
292
+ return JSON.parse(data) as MessageData;
293
+ } catch (error) {
294
+ console.error(`Error parsing message data for id ${id}:`, error);
295
+ return null;
296
+ }
297
+ }
298
+
299
+ async findBySessionId(
300
+ sessionId: string,
301
+ limit = 1000
302
+ ): Promise<MessageData[]> {
303
+ const messageIds = await this.redis.hgetall(this.getSessionKey(sessionId));
304
+ const messages: MessageData[] = [];
305
+
306
+ for (const messageId of Object.keys(messageIds).slice(0, limit)) {
307
+ const message = await this.findById(messageId);
308
+ if (message) {
309
+ messages.push(message);
310
+ }
311
+ }
312
+
313
+ return messages.sort(
314
+ (a, b) => a.createdAt.getTime() - b.createdAt.getTime()
315
+ );
316
+ }
317
+
318
+ async findByUserId(userId: string, limit = 100): Promise<MessageData[]> {
319
+ // Redis doesn't have efficient user-level querying
320
+ // This would require additional indexing
321
+ const pattern = `${this.keyPrefix}message:*`;
322
+ const keys = await this.redis.keys(pattern);
323
+ const messages: MessageData[] = [];
324
+
325
+ for (const key of keys.slice(0, limit)) {
326
+ const data = await this.redis.get(key);
327
+ if (data) {
328
+ const message: MessageData = JSON.parse(data) as MessageData;
329
+ if (message.userId === userId) {
330
+ messages.push(message);
331
+ }
332
+ }
333
+ }
334
+
335
+ return messages.sort(
336
+ (a, b) => b.createdAt.getTime() - a.createdAt.getTime()
337
+ );
338
+ }
339
+
340
+ async delete(id: string): Promise<boolean> {
341
+ const result = await this.redis.del(this.getKey(id));
342
+ return result > 0;
343
+ }
344
+
345
+ async deleteBySessionId(sessionId: string): Promise<number> {
346
+ const messageIds = await this.redis.hgetall(this.getSessionKey(sessionId));
347
+ const keys = Object.keys(messageIds).map((id) => this.getKey(id));
348
+
349
+ if (keys.length === 0) return 0;
350
+
351
+ const result = await this.redis.del(...keys);
352
+ await this.redis.del(this.getSessionKey(sessionId));
353
+
354
+ return result;
355
+ }
356
+
357
+ async deleteByUserId(userId: string): Promise<number> {
358
+ const messages = await this.findByUserId(userId);
359
+ const keys = messages.map((m) => this.getKey(m.id));
360
+
361
+ if (keys.length === 0) return 0;
362
+
363
+ return await this.redis.del(...keys);
364
+ }
365
+ }