@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@falai/agent",
3
- "version": "0.3.12",
3
+ "version": "0.3.21",
4
4
  "description": "Standalone, strongly-typed AI Agent framework with route DSL and AI provider strategy",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -82,11 +82,35 @@
82
82
  "openai": "^6.3.0"
83
83
  },
84
84
  "peerDependencies": {
85
- "@prisma/client": "^5.0.0 || ^6.0.0"
85
+ "@prisma/client": "^6.0.0",
86
+ "ioredis": "^5.7.0",
87
+ "redis": "^4.6.0 || ^5.0.0",
88
+ "mongodb": "^6.0.0 || ^7.0.0",
89
+ "pg": "^8.11.0",
90
+ "mysql2": "^3.2.0",
91
+ "better-sqlite3": "^11.0.0 || ^12.0.0"
86
92
  },
87
93
  "peerDependenciesMeta": {
88
94
  "@prisma/client": {
89
95
  "optional": true
96
+ },
97
+ "ioredis": {
98
+ "optional": true
99
+ },
100
+ "redis": {
101
+ "optional": true
102
+ },
103
+ "mongodb": {
104
+ "optional": true
105
+ },
106
+ "pg": {
107
+ "optional": true
108
+ },
109
+ "mysql2": {
110
+ "optional": true
111
+ },
112
+ "better-sqlite3": {
113
+ "optional": true
90
114
  }
91
115
  }
92
116
  }
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Memory adapter for persistence
3
+ * In-memory storage for testing and development (no database required)
4
+ */
5
+
6
+ import type {
7
+ SessionRepository,
8
+ MessageRepository,
9
+ SessionData,
10
+ MessageData,
11
+ SessionStatus,
12
+ PersistenceAdapter,
13
+ } from "../types/persistence";
14
+
15
+ /**
16
+ * Memory Adapter - Provider-style API for in-memory persistence
17
+ *
18
+ * Perfect for:
19
+ * - Testing
20
+ * - Development
21
+ * - Prototyping
22
+ * - No database setup required
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { Agent, MemoryAdapter } from '@falai/agent';
27
+ *
28
+ * const agent = new Agent({
29
+ * name: "My Agent",
30
+ * ai: provider,
31
+ * persistence: {
32
+ * adapter: new MemoryAdapter(),
33
+ * userId: "user_123",
34
+ * },
35
+ * });
36
+ * ```
37
+ */
38
+ export class MemoryAdapter implements PersistenceAdapter {
39
+ public readonly sessionRepository: SessionRepository;
40
+ public readonly messageRepository: MessageRepository;
41
+ private sessions: Map<string, SessionData>;
42
+ private messages: Map<string, MessageData>;
43
+
44
+ constructor() {
45
+ this.sessions = new Map();
46
+ this.messages = new Map();
47
+
48
+ this.sessionRepository = new MemorySessionRepository(this.sessions);
49
+ this.messageRepository = new MemoryMessageRepository(this.messages);
50
+ }
51
+
52
+ /**
53
+ * Clear all data (useful for testing)
54
+ */
55
+ clear(): void {
56
+ this.sessions.clear();
57
+ this.messages.clear();
58
+ }
59
+
60
+ /**
61
+ * Get data snapshot (useful for debugging)
62
+ */
63
+ getSnapshot(): {
64
+ sessions: SessionData[];
65
+ messages: MessageData[];
66
+ } {
67
+ return {
68
+ sessions: Array.from(this.sessions.values()),
69
+ messages: Array.from(this.messages.values()),
70
+ };
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Memory Session Repository
76
+ */
77
+ class MemorySessionRepository implements SessionRepository {
78
+ constructor(private sessions: Map<string, SessionData>) {}
79
+
80
+ async create(
81
+ data: Omit<SessionData, "id" | "createdAt" | "updatedAt">
82
+ ): Promise<SessionData> {
83
+ const id = `session_${Date.now()}_${Math.random().toString(36).slice(2)}`;
84
+ const now = new Date();
85
+
86
+ const session: SessionData = {
87
+ ...data,
88
+ id,
89
+ status: data.status || "active",
90
+ messageCount: data.messageCount || 0,
91
+ createdAt: now,
92
+ updatedAt: now,
93
+ };
94
+
95
+ this.sessions.set(id, session);
96
+ return Promise.resolve(session);
97
+ }
98
+
99
+ async findById(id: string): Promise<SessionData | null> {
100
+ const session = this.sessions.get(id) || null;
101
+ return Promise.resolve(session);
102
+ }
103
+
104
+ async findActiveByUserId(userId: string): Promise<SessionData | null> {
105
+ const sessions = Array.from(this.sessions.values())
106
+ .filter((s) => s.userId === userId && s.status === "active")
107
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
108
+
109
+ return Promise.resolve(sessions[0] || null);
110
+ }
111
+
112
+ async findByUserId(userId: string, limit = 100): Promise<SessionData[]> {
113
+ const sessions = Array.from(this.sessions.values())
114
+ .filter((s) => s.userId === userId)
115
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
116
+ .slice(0, limit);
117
+ return Promise.resolve(sessions);
118
+ }
119
+
120
+ async update(
121
+ id: string,
122
+ data: Partial<Omit<SessionData, "id" | "createdAt">>
123
+ ): Promise<SessionData | null> {
124
+ const existing = this.sessions.get(id);
125
+ if (!existing) return null;
126
+
127
+ const updated: SessionData = {
128
+ ...existing,
129
+ ...data,
130
+ updatedAt: new Date(),
131
+ };
132
+
133
+ this.sessions.set(id, updated);
134
+ return Promise.resolve(updated);
135
+ }
136
+
137
+ async updateStatus(
138
+ id: string,
139
+ status: SessionStatus,
140
+ completedAt?: Date
141
+ ): Promise<SessionData | null> {
142
+ return await this.update(id, { status, completedAt });
143
+ }
144
+
145
+ async updateCollectedData(
146
+ id: string,
147
+ collectedData: Record<string, unknown>
148
+ ): Promise<SessionData | null> {
149
+ return await this.update(id, { collectedData });
150
+ }
151
+
152
+ async updateRouteState(
153
+ id: string,
154
+ route?: string,
155
+ state?: string
156
+ ): Promise<SessionData | null> {
157
+ return await this.update(id, {
158
+ currentRoute: route,
159
+ currentState: state,
160
+ });
161
+ }
162
+
163
+ async incrementMessageCount(id: string): Promise<SessionData | null> {
164
+ const session = this.sessions.get(id);
165
+ if (!session) return null;
166
+
167
+ return await this.update(id, {
168
+ messageCount: (session.messageCount || 0) + 1,
169
+ lastMessageAt: new Date(),
170
+ });
171
+ }
172
+
173
+ async delete(id: string): Promise<boolean> {
174
+ const result = this.sessions.delete(id);
175
+ return Promise.resolve(result);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Memory Message Repository
181
+ */
182
+ class MemoryMessageRepository implements MessageRepository {
183
+ constructor(private messages: Map<string, MessageData>) {}
184
+
185
+ async create(
186
+ data: Omit<MessageData, "id" | "createdAt">
187
+ ): Promise<MessageData> {
188
+ const id = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
189
+ const message: MessageData = {
190
+ ...data,
191
+ id,
192
+ createdAt: new Date(),
193
+ };
194
+
195
+ this.messages.set(id, message);
196
+ return Promise.resolve(message);
197
+ }
198
+
199
+ async findById(id: string): Promise<MessageData | null> {
200
+ const message = this.messages.get(id) || null;
201
+ return Promise.resolve(message);
202
+ }
203
+
204
+ async findBySessionId(
205
+ sessionId: string,
206
+ limit = 1000
207
+ ): Promise<MessageData[]> {
208
+ const messages = Array.from(this.messages.values())
209
+ .filter((m) => m.sessionId === sessionId)
210
+ .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
211
+ .slice(0, limit);
212
+ return Promise.resolve(messages);
213
+ }
214
+
215
+ async findByUserId(userId: string, limit = 100): Promise<MessageData[]> {
216
+ const messages = Array.from(this.messages.values())
217
+ .filter((m) => m.userId === userId)
218
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
219
+ .slice(0, limit);
220
+ return Promise.resolve(messages);
221
+ }
222
+
223
+ async delete(id: string): Promise<boolean> {
224
+ const result = this.messages.delete(id);
225
+ return Promise.resolve(result);
226
+ }
227
+
228
+ async deleteBySessionId(sessionId: string): Promise<number> {
229
+ const toDelete = Array.from(this.messages.values()).filter(
230
+ (m) => m.sessionId === sessionId
231
+ );
232
+
233
+ toDelete.forEach((m) => this.messages.delete(m.id));
234
+ return Promise.resolve(toDelete.length);
235
+ }
236
+
237
+ async deleteByUserId(userId: string): Promise<number> {
238
+ const toDelete = Array.from(this.messages.values()).filter(
239
+ (m) => m.userId === userId
240
+ );
241
+
242
+ toDelete.forEach((m) => this.messages.delete(m.id));
243
+ return Promise.resolve(toDelete.length);
244
+ }
245
+ }
@@ -0,0 +1,295 @@
1
+ /**
2
+ * MongoDB adapter for persistence
3
+ * Document-based storage with flexible schema
4
+ */
5
+
6
+ import type {
7
+ SessionRepository,
8
+ MessageRepository,
9
+ SessionData,
10
+ MessageData,
11
+ SessionStatus,
12
+ PersistenceAdapter,
13
+ } from "../types/persistence";
14
+
15
+ /**
16
+ * MongoDB collection interface - matches mongodb driver
17
+ */
18
+ export interface MongoCollection<T = Record<string, unknown>> {
19
+ insertOne(doc: T): Promise<{ insertedId: unknown }>;
20
+ findOne(filter: Record<string, unknown>): Promise<T | null>;
21
+ find(filter: Record<string, unknown>): {
22
+ sort(sort: Record<string, number>): {
23
+ limit(limit: number): {
24
+ toArray(): Promise<T[]>;
25
+ };
26
+ };
27
+ toArray(): Promise<T[]>;
28
+ };
29
+ updateOne(
30
+ filter: Record<string, unknown>,
31
+ update: Record<string, unknown>
32
+ ): Promise<{ matchedCount: number }>;
33
+ deleteOne(filter: Record<string, unknown>): Promise<{ deletedCount: number }>;
34
+ deleteMany(
35
+ filter: Record<string, unknown>
36
+ ): Promise<{ deletedCount: number }>;
37
+ }
38
+
39
+ /**
40
+ * MongoDB database interface
41
+ */
42
+ export interface MongoDatabase {
43
+ collection<T = Record<string, unknown>>(name: string): MongoCollection<T>;
44
+ }
45
+
46
+ /**
47
+ * MongoDB client interface
48
+ */
49
+ export interface MongoClient {
50
+ db(name?: string): MongoDatabase;
51
+ close(): Promise<void>;
52
+ }
53
+
54
+ /**
55
+ * Options for MongoDB adapter
56
+ */
57
+ export interface MongoAdapterOptions {
58
+ /**
59
+ * MongoDB client instance
60
+ */
61
+ client: MongoClient;
62
+
63
+ /**
64
+ * Database name
65
+ */
66
+ databaseName: string;
67
+
68
+ /**
69
+ * Collection names (default: "agent_sessions" and "agent_messages")
70
+ */
71
+ collections?: {
72
+ sessions?: string;
73
+ messages?: string;
74
+ };
75
+ }
76
+
77
+ /**
78
+ * MongoDB Adapter - Provider-style API for MongoDB persistence
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { MongoClient } from 'mongodb';
83
+ * import { Agent, MongoAdapter } from '@falai/agent';
84
+ *
85
+ * const client = new MongoClient('mongodb://localhost:27017');
86
+ * await client.connect();
87
+ *
88
+ * const agent = new Agent({
89
+ * name: "My Agent",
90
+ * ai: provider,
91
+ * persistence: {
92
+ * adapter: new MongoAdapter({
93
+ * client,
94
+ * databaseName: 'myapp',
95
+ * }),
96
+ * userId: "user_123",
97
+ * },
98
+ * });
99
+ * ```
100
+ */
101
+ export class MongoAdapter implements PersistenceAdapter {
102
+ public readonly sessionRepository: SessionRepository;
103
+ public readonly messageRepository: MessageRepository;
104
+ private client: MongoClient;
105
+ private db: MongoDatabase;
106
+
107
+ constructor(options: MongoAdapterOptions) {
108
+ this.client = options.client;
109
+ this.db = options.client.db(options.databaseName);
110
+
111
+ const sessionCollection = options.collections?.sessions || "agent_sessions";
112
+ const messageCollection = options.collections?.messages || "agent_messages";
113
+
114
+ this.sessionRepository = new MongoSessionRepository(
115
+ this.db.collection(sessionCollection)
116
+ );
117
+
118
+ this.messageRepository = new MongoMessageRepository(
119
+ this.db.collection(messageCollection)
120
+ );
121
+ }
122
+
123
+ async disconnect(): Promise<void> {
124
+ await this.client.close();
125
+ }
126
+ }
127
+
128
+ /**
129
+ * MongoDB Session Repository
130
+ */
131
+ class MongoSessionRepository implements SessionRepository {
132
+ constructor(private collection: MongoCollection<SessionData>) {}
133
+
134
+ async create(
135
+ data: Omit<SessionData, "id" | "createdAt" | "updatedAt">
136
+ ): Promise<SessionData> {
137
+ const now = new Date();
138
+ const session: SessionData = {
139
+ ...data,
140
+ id: `session_${Date.now()}_${Math.random().toString(36).slice(2)}`,
141
+ status: data.status || "active",
142
+ messageCount: data.messageCount || 0,
143
+ createdAt: now,
144
+ updatedAt: now,
145
+ };
146
+
147
+ await this.collection.insertOne(session);
148
+ return session;
149
+ }
150
+
151
+ async findById(id: string): Promise<SessionData | null> {
152
+ return await this.collection.findOne({ id });
153
+ }
154
+
155
+ async findActiveByUserId(userId: string): Promise<SessionData | null> {
156
+ return await this.collection.findOne({ userId, status: "active" });
157
+ }
158
+
159
+ async findByUserId(userId: string, limit = 100): Promise<SessionData[]> {
160
+ return await this.collection
161
+ .find({ userId })
162
+ .sort({ createdAt: -1 })
163
+ .limit(limit)
164
+ .toArray();
165
+ }
166
+
167
+ async update(
168
+ id: string,
169
+ data: Partial<Omit<SessionData, "id" | "createdAt">>
170
+ ): Promise<SessionData | null> {
171
+ const result = await this.collection.updateOne(
172
+ { id },
173
+ { $set: { ...data, updatedAt: new Date() } }
174
+ );
175
+
176
+ if (result.matchedCount === 0) return null;
177
+ return await this.findById(id);
178
+ }
179
+
180
+ async updateStatus(
181
+ id: string,
182
+ status: SessionStatus,
183
+ completedAt?: Date
184
+ ): Promise<SessionData | null> {
185
+ const updateData: Record<string, unknown> = {
186
+ status,
187
+ updatedAt: new Date(),
188
+ };
189
+ if (completedAt) {
190
+ updateData.completedAt = completedAt;
191
+ }
192
+
193
+ const result = await this.collection.updateOne(
194
+ { id },
195
+ { $set: updateData }
196
+ );
197
+
198
+ if (result.matchedCount === 0) return null;
199
+ return await this.findById(id);
200
+ }
201
+
202
+ async updateCollectedData(
203
+ id: string,
204
+ collectedData: Record<string, unknown>
205
+ ): Promise<SessionData | null> {
206
+ return await this.update(id, { collectedData });
207
+ }
208
+
209
+ async updateRouteState(
210
+ id: string,
211
+ route?: string,
212
+ state?: string
213
+ ): Promise<SessionData | null> {
214
+ return await this.update(id, {
215
+ currentRoute: route,
216
+ currentState: state,
217
+ });
218
+ }
219
+
220
+ async incrementMessageCount(id: string): Promise<SessionData | null> {
221
+ const result = await this.collection.updateOne(
222
+ { id },
223
+ {
224
+ $inc: { messageCount: 1 },
225
+ $set: { lastMessageAt: new Date(), updatedAt: new Date() },
226
+ }
227
+ );
228
+
229
+ if (result.matchedCount === 0) return null;
230
+ return await this.findById(id);
231
+ }
232
+
233
+ async delete(id: string): Promise<boolean> {
234
+ const result = await this.collection.deleteOne({ id });
235
+ return result.deletedCount > 0;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * MongoDB Message Repository
241
+ */
242
+ class MongoMessageRepository implements MessageRepository {
243
+ constructor(private collection: MongoCollection<MessageData>) {}
244
+
245
+ async create(
246
+ data: Omit<MessageData, "id" | "createdAt">
247
+ ): Promise<MessageData> {
248
+ const message: MessageData = {
249
+ ...data,
250
+ id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
251
+ createdAt: new Date(),
252
+ };
253
+
254
+ await this.collection.insertOne(message);
255
+ return message;
256
+ }
257
+
258
+ async findById(id: string): Promise<MessageData | null> {
259
+ return await this.collection.findOne({ id });
260
+ }
261
+
262
+ async findBySessionId(
263
+ sessionId: string,
264
+ limit = 1000
265
+ ): Promise<MessageData[]> {
266
+ return await this.collection
267
+ .find({ sessionId })
268
+ .sort({ createdAt: 1 })
269
+ .limit(limit)
270
+ .toArray();
271
+ }
272
+
273
+ async findByUserId(userId: string, limit = 100): Promise<MessageData[]> {
274
+ return await this.collection
275
+ .find({ userId })
276
+ .sort({ createdAt: -1 })
277
+ .limit(limit)
278
+ .toArray();
279
+ }
280
+
281
+ async delete(id: string): Promise<boolean> {
282
+ const result = await this.collection.deleteOne({ id });
283
+ return result.deletedCount > 0;
284
+ }
285
+
286
+ async deleteBySessionId(sessionId: string): Promise<number> {
287
+ const result = await this.collection.deleteMany({ sessionId });
288
+ return result.deletedCount;
289
+ }
290
+
291
+ async deleteByUserId(userId: string): Promise<number> {
292
+ const result = await this.collection.deleteMany({ userId });
293
+ return result.deletedCount;
294
+ }
295
+ }