@snap-agent/core 0.1.0
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/LICENSE +22 -0
- package/README.md +625 -0
- package/dist/chunk-V4TPAVOY.mjs +2227 -0
- package/dist/chunk-Y5TTFQWC.mjs +801 -0
- package/dist/dist-2CMI4QQD.mjs +1825 -0
- package/dist/dist-JU54Y3G4.mjs +3216 -0
- package/dist/index-CDsqnM8L.d.mts +680 -0
- package/dist/index-CDsqnM8L.d.ts +680 -0
- package/dist/index.d.mts +472 -0
- package/dist/index.d.ts +472 -0
- package/dist/index.js +9413 -0
- package/dist/index.mjs +1284 -0
- package/dist/storage/index.d.mts +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +829 -0
- package/dist/storage/index.mjs +10 -0
- package/package.json +114 -0
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/storage/index.ts
|
|
21
|
+
var storage_exports = {};
|
|
22
|
+
__export(storage_exports, {
|
|
23
|
+
MemoryStorage: () => MemoryStorage,
|
|
24
|
+
MongoDBStorage: () => MongoDBStorage,
|
|
25
|
+
UpstashStorage: () => UpstashStorage
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(storage_exports);
|
|
28
|
+
|
|
29
|
+
// src/storage/MongoDBStorage.ts
|
|
30
|
+
var import_mongodb = require("mongodb");
|
|
31
|
+
var MongoDBStorage = class {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.db = null;
|
|
34
|
+
if (typeof config === "string") {
|
|
35
|
+
this.config = {
|
|
36
|
+
uri: config,
|
|
37
|
+
dbName: "agentStudio",
|
|
38
|
+
agentsCollection: "v2_agents",
|
|
39
|
+
threadsCollection: "v2_threads"
|
|
40
|
+
};
|
|
41
|
+
} else {
|
|
42
|
+
this.config = {
|
|
43
|
+
uri: config.uri,
|
|
44
|
+
dbName: config.dbName || "agentStudio",
|
|
45
|
+
agentsCollection: config.agentsCollection || "v2_agents",
|
|
46
|
+
threadsCollection: config.threadsCollection || "v2_threads"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
this.client = new import_mongodb.MongoClient(this.config.uri);
|
|
50
|
+
}
|
|
51
|
+
async ensureConnection() {
|
|
52
|
+
if (!this.db) {
|
|
53
|
+
await this.client.connect();
|
|
54
|
+
this.db = this.client.db(this.config.dbName);
|
|
55
|
+
}
|
|
56
|
+
return this.db;
|
|
57
|
+
}
|
|
58
|
+
async disconnect() {
|
|
59
|
+
await this.client.close();
|
|
60
|
+
this.db = null;
|
|
61
|
+
}
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Agent Operations
|
|
64
|
+
// ============================================================================
|
|
65
|
+
async createAgent(config) {
|
|
66
|
+
const db = await this.ensureConnection();
|
|
67
|
+
const collection = db.collection(
|
|
68
|
+
this.config.agentsCollection
|
|
69
|
+
);
|
|
70
|
+
const doc = {
|
|
71
|
+
organizationId: config.organizationId,
|
|
72
|
+
userId: config.userId,
|
|
73
|
+
phone: config.phone,
|
|
74
|
+
name: config.name,
|
|
75
|
+
description: config.description,
|
|
76
|
+
instructions: config.instructions,
|
|
77
|
+
provider: config.provider,
|
|
78
|
+
model: config.model,
|
|
79
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
80
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
81
|
+
files: [],
|
|
82
|
+
metadata: config.metadata || {}
|
|
83
|
+
};
|
|
84
|
+
const result = await collection.insertOne(doc);
|
|
85
|
+
return result.insertedId.toString();
|
|
86
|
+
}
|
|
87
|
+
async getAgent(agentId) {
|
|
88
|
+
const db = await this.ensureConnection();
|
|
89
|
+
const collection = db.collection(
|
|
90
|
+
this.config.agentsCollection
|
|
91
|
+
);
|
|
92
|
+
const doc = await collection.findOne({ _id: new import_mongodb.ObjectId(agentId) });
|
|
93
|
+
if (!doc) return null;
|
|
94
|
+
return this.agentDocToData(doc);
|
|
95
|
+
}
|
|
96
|
+
async updateAgent(agentId, updates) {
|
|
97
|
+
const db = await this.ensureConnection();
|
|
98
|
+
const collection = db.collection(
|
|
99
|
+
this.config.agentsCollection
|
|
100
|
+
);
|
|
101
|
+
await collection.updateOne(
|
|
102
|
+
{ _id: new import_mongodb.ObjectId(agentId) },
|
|
103
|
+
{
|
|
104
|
+
$set: {
|
|
105
|
+
...updates,
|
|
106
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
async deleteAgent(agentId) {
|
|
112
|
+
const db = await this.ensureConnection();
|
|
113
|
+
const collection = db.collection(
|
|
114
|
+
this.config.agentsCollection
|
|
115
|
+
);
|
|
116
|
+
await collection.deleteOne({ _id: new import_mongodb.ObjectId(agentId) });
|
|
117
|
+
}
|
|
118
|
+
async listAgents(userId, organizationId) {
|
|
119
|
+
const db = await this.ensureConnection();
|
|
120
|
+
const collection = db.collection(
|
|
121
|
+
this.config.agentsCollection
|
|
122
|
+
);
|
|
123
|
+
const query = { userId };
|
|
124
|
+
if (organizationId) {
|
|
125
|
+
query.organizationId = organizationId;
|
|
126
|
+
}
|
|
127
|
+
const docs = await collection.find(query).sort({ updatedAt: -1 }).toArray();
|
|
128
|
+
return docs.map((doc) => this.agentDocToData(doc));
|
|
129
|
+
}
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Thread Operations
|
|
132
|
+
// ============================================================================
|
|
133
|
+
async createThread(config) {
|
|
134
|
+
const db = await this.ensureConnection();
|
|
135
|
+
const collection = db.collection(
|
|
136
|
+
this.config.threadsCollection
|
|
137
|
+
);
|
|
138
|
+
const doc = {
|
|
139
|
+
organizationId: config.organizationId,
|
|
140
|
+
agentId: config.agentId,
|
|
141
|
+
userId: config.userId,
|
|
142
|
+
endUserId: config.endUserId,
|
|
143
|
+
name: config.name,
|
|
144
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
145
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
146
|
+
messages: [],
|
|
147
|
+
isPendingThread: true,
|
|
148
|
+
metadata: config.metadata || {}
|
|
149
|
+
};
|
|
150
|
+
const result = await collection.insertOne(doc);
|
|
151
|
+
return result.insertedId.toString();
|
|
152
|
+
}
|
|
153
|
+
async getThread(threadId) {
|
|
154
|
+
const db = await this.ensureConnection();
|
|
155
|
+
const collection = db.collection(
|
|
156
|
+
this.config.threadsCollection
|
|
157
|
+
);
|
|
158
|
+
const doc = await collection.findOne({ _id: new import_mongodb.ObjectId(threadId) });
|
|
159
|
+
if (!doc) return null;
|
|
160
|
+
return this.threadDocToData(doc);
|
|
161
|
+
}
|
|
162
|
+
async updateThread(threadId, updates) {
|
|
163
|
+
const db = await this.ensureConnection();
|
|
164
|
+
const collection = db.collection(
|
|
165
|
+
this.config.threadsCollection
|
|
166
|
+
);
|
|
167
|
+
await collection.updateOne(
|
|
168
|
+
{ _id: new import_mongodb.ObjectId(threadId) },
|
|
169
|
+
{
|
|
170
|
+
$set: {
|
|
171
|
+
...updates,
|
|
172
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
async deleteThread(threadId) {
|
|
178
|
+
const db = await this.ensureConnection();
|
|
179
|
+
const collection = db.collection(
|
|
180
|
+
this.config.threadsCollection
|
|
181
|
+
);
|
|
182
|
+
await collection.deleteOne({ _id: new import_mongodb.ObjectId(threadId) });
|
|
183
|
+
}
|
|
184
|
+
async listThreads(filters) {
|
|
185
|
+
const db = await this.ensureConnection();
|
|
186
|
+
const collection = db.collection(
|
|
187
|
+
this.config.threadsCollection
|
|
188
|
+
);
|
|
189
|
+
const query = {};
|
|
190
|
+
if (filters.userId) query.userId = filters.userId;
|
|
191
|
+
if (filters.agentId) query.agentId = filters.agentId;
|
|
192
|
+
if (filters.organizationId) query.organizationId = filters.organizationId;
|
|
193
|
+
const docs = await collection.find(query).sort({ updatedAt: -1 }).toArray();
|
|
194
|
+
return docs.map((doc) => this.threadDocToData(doc));
|
|
195
|
+
}
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Message Operations
|
|
198
|
+
// ============================================================================
|
|
199
|
+
async addMessage(threadId, role, content, attachments) {
|
|
200
|
+
const db = await this.ensureConnection();
|
|
201
|
+
const collection = db.collection(
|
|
202
|
+
this.config.threadsCollection
|
|
203
|
+
);
|
|
204
|
+
const messageId = new import_mongodb.ObjectId();
|
|
205
|
+
const message = {
|
|
206
|
+
_id: messageId,
|
|
207
|
+
role,
|
|
208
|
+
content,
|
|
209
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
210
|
+
attachments
|
|
211
|
+
};
|
|
212
|
+
await collection.updateOne(
|
|
213
|
+
{ _id: new import_mongodb.ObjectId(threadId) },
|
|
214
|
+
{
|
|
215
|
+
$push: { messages: message },
|
|
216
|
+
$set: { updatedAt: /* @__PURE__ */ new Date() }
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
return messageId.toString();
|
|
220
|
+
}
|
|
221
|
+
async getMessages(threadId, limit) {
|
|
222
|
+
const db = await this.ensureConnection();
|
|
223
|
+
const collection = db.collection(
|
|
224
|
+
this.config.threadsCollection
|
|
225
|
+
);
|
|
226
|
+
const doc = await collection.findOne({ _id: new import_mongodb.ObjectId(threadId) });
|
|
227
|
+
if (!doc) return [];
|
|
228
|
+
let messages = doc.messages.map((msg) => ({
|
|
229
|
+
id: msg._id?.toString() || "",
|
|
230
|
+
role: msg.role,
|
|
231
|
+
content: msg.content,
|
|
232
|
+
timestamp: msg.timestamp,
|
|
233
|
+
metadata: msg.metadata,
|
|
234
|
+
attachments: msg.attachments
|
|
235
|
+
}));
|
|
236
|
+
if (limit) {
|
|
237
|
+
messages = messages.slice(-limit);
|
|
238
|
+
}
|
|
239
|
+
return messages;
|
|
240
|
+
}
|
|
241
|
+
async getConversationContext(threadId, maxMessages = 20) {
|
|
242
|
+
const messages = await this.getMessages(threadId, maxMessages);
|
|
243
|
+
return messages.map((msg) => ({
|
|
244
|
+
role: msg.role,
|
|
245
|
+
content: msg.content
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
// ============================================================================
|
|
249
|
+
// Helper Methods
|
|
250
|
+
// ============================================================================
|
|
251
|
+
agentDocToData(doc) {
|
|
252
|
+
return {
|
|
253
|
+
id: doc._id.toString(),
|
|
254
|
+
organizationId: doc.organizationId,
|
|
255
|
+
userId: doc.userId,
|
|
256
|
+
phone: doc.phone,
|
|
257
|
+
name: doc.name,
|
|
258
|
+
description: doc.description,
|
|
259
|
+
instructions: doc.instructions,
|
|
260
|
+
provider: doc.provider,
|
|
261
|
+
model: doc.model,
|
|
262
|
+
createdAt: doc.createdAt,
|
|
263
|
+
updatedAt: doc.updatedAt,
|
|
264
|
+
files: doc.files,
|
|
265
|
+
metadata: doc.metadata
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
threadDocToData(doc) {
|
|
269
|
+
return {
|
|
270
|
+
id: doc._id.toString(),
|
|
271
|
+
organizationId: doc.organizationId,
|
|
272
|
+
agentId: doc.agentId,
|
|
273
|
+
userId: doc.userId,
|
|
274
|
+
endUserId: doc.endUserId,
|
|
275
|
+
name: doc.name,
|
|
276
|
+
createdAt: doc.createdAt,
|
|
277
|
+
updatedAt: doc.updatedAt,
|
|
278
|
+
messages: doc.messages.map((msg) => ({
|
|
279
|
+
id: msg._id?.toString() || "",
|
|
280
|
+
role: msg.role,
|
|
281
|
+
content: msg.content,
|
|
282
|
+
timestamp: msg.timestamp,
|
|
283
|
+
metadata: msg.metadata,
|
|
284
|
+
attachments: msg.attachments
|
|
285
|
+
})),
|
|
286
|
+
isPendingThread: doc.isPendingThread,
|
|
287
|
+
metadata: doc.metadata
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// src/storage/MemoryStorage.ts
|
|
293
|
+
var MemoryStorage = class {
|
|
294
|
+
constructor() {
|
|
295
|
+
this.agents = /* @__PURE__ */ new Map();
|
|
296
|
+
this.threads = /* @__PURE__ */ new Map();
|
|
297
|
+
this.idCounter = 0;
|
|
298
|
+
}
|
|
299
|
+
generateId() {
|
|
300
|
+
return `${Date.now()}-${++this.idCounter}`;
|
|
301
|
+
}
|
|
302
|
+
// ============================================================================
|
|
303
|
+
// Agent Operations
|
|
304
|
+
// ============================================================================
|
|
305
|
+
async createAgent(config) {
|
|
306
|
+
const id = this.generateId();
|
|
307
|
+
const agent = {
|
|
308
|
+
id,
|
|
309
|
+
...config,
|
|
310
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
311
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
312
|
+
files: []
|
|
313
|
+
};
|
|
314
|
+
this.agents.set(id, agent);
|
|
315
|
+
return id;
|
|
316
|
+
}
|
|
317
|
+
async getAgent(agentId) {
|
|
318
|
+
return this.agents.get(agentId) || null;
|
|
319
|
+
}
|
|
320
|
+
async updateAgent(agentId, updates) {
|
|
321
|
+
const agent = this.agents.get(agentId);
|
|
322
|
+
if (!agent) return;
|
|
323
|
+
Object.assign(agent, updates, { updatedAt: /* @__PURE__ */ new Date() });
|
|
324
|
+
this.agents.set(agentId, agent);
|
|
325
|
+
}
|
|
326
|
+
async deleteAgent(agentId) {
|
|
327
|
+
this.agents.delete(agentId);
|
|
328
|
+
const threadsToDelete = [];
|
|
329
|
+
for (const [threadId, thread] of this.threads.entries()) {
|
|
330
|
+
if (thread.agentId === agentId) {
|
|
331
|
+
threadsToDelete.push(threadId);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
for (const threadId of threadsToDelete) {
|
|
335
|
+
this.threads.delete(threadId);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async listAgents(userId, organizationId) {
|
|
339
|
+
const agents = Array.from(this.agents.values()).filter((agent) => {
|
|
340
|
+
if (agent.userId !== userId) return false;
|
|
341
|
+
if (organizationId && agent.organizationId !== organizationId) return false;
|
|
342
|
+
return true;
|
|
343
|
+
});
|
|
344
|
+
return agents.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
345
|
+
}
|
|
346
|
+
// ============================================================================
|
|
347
|
+
// Thread Operations
|
|
348
|
+
// ============================================================================
|
|
349
|
+
async createThread(config) {
|
|
350
|
+
const id = this.generateId();
|
|
351
|
+
const thread = {
|
|
352
|
+
id,
|
|
353
|
+
...config,
|
|
354
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
355
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
356
|
+
messages: [],
|
|
357
|
+
isPendingThread: true
|
|
358
|
+
};
|
|
359
|
+
this.threads.set(id, thread);
|
|
360
|
+
return id;
|
|
361
|
+
}
|
|
362
|
+
async getThread(threadId) {
|
|
363
|
+
return this.threads.get(threadId) || null;
|
|
364
|
+
}
|
|
365
|
+
async updateThread(threadId, updates) {
|
|
366
|
+
const thread = this.threads.get(threadId);
|
|
367
|
+
if (!thread) return;
|
|
368
|
+
Object.assign(thread, updates, { updatedAt: /* @__PURE__ */ new Date() });
|
|
369
|
+
this.threads.set(threadId, thread);
|
|
370
|
+
}
|
|
371
|
+
async deleteThread(threadId) {
|
|
372
|
+
this.threads.delete(threadId);
|
|
373
|
+
}
|
|
374
|
+
async listThreads(filters) {
|
|
375
|
+
const threads = Array.from(this.threads.values()).filter((thread) => {
|
|
376
|
+
if (filters.userId && thread.userId !== filters.userId) return false;
|
|
377
|
+
if (filters.agentId && thread.agentId !== filters.agentId) return false;
|
|
378
|
+
if (filters.organizationId && thread.organizationId !== filters.organizationId) return false;
|
|
379
|
+
return true;
|
|
380
|
+
});
|
|
381
|
+
return threads.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
382
|
+
}
|
|
383
|
+
// ============================================================================
|
|
384
|
+
// Message Operations
|
|
385
|
+
// ============================================================================
|
|
386
|
+
async addMessage(threadId, role, content, attachments) {
|
|
387
|
+
const thread = this.threads.get(threadId);
|
|
388
|
+
if (!thread) {
|
|
389
|
+
throw new Error(`Thread not found: ${threadId}`);
|
|
390
|
+
}
|
|
391
|
+
const messageId = this.generateId();
|
|
392
|
+
const message = {
|
|
393
|
+
id: messageId,
|
|
394
|
+
role,
|
|
395
|
+
content,
|
|
396
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
397
|
+
attachments
|
|
398
|
+
};
|
|
399
|
+
thread.messages.push(message);
|
|
400
|
+
thread.updatedAt = /* @__PURE__ */ new Date();
|
|
401
|
+
this.threads.set(threadId, thread);
|
|
402
|
+
return messageId;
|
|
403
|
+
}
|
|
404
|
+
async getMessages(threadId, limit) {
|
|
405
|
+
const thread = this.threads.get(threadId);
|
|
406
|
+
if (!thread) return [];
|
|
407
|
+
const messages = [...thread.messages];
|
|
408
|
+
if (limit) {
|
|
409
|
+
return messages.slice(-limit);
|
|
410
|
+
}
|
|
411
|
+
return messages;
|
|
412
|
+
}
|
|
413
|
+
async getConversationContext(threadId, maxMessages = 20) {
|
|
414
|
+
const messages = await this.getMessages(threadId, maxMessages);
|
|
415
|
+
return messages.map((msg) => ({
|
|
416
|
+
role: msg.role,
|
|
417
|
+
content: msg.content
|
|
418
|
+
}));
|
|
419
|
+
}
|
|
420
|
+
// ============================================================================
|
|
421
|
+
// Utility Methods
|
|
422
|
+
// ============================================================================
|
|
423
|
+
/**
|
|
424
|
+
* Clear all stored data
|
|
425
|
+
*/
|
|
426
|
+
clear() {
|
|
427
|
+
this.agents.clear();
|
|
428
|
+
this.threads.clear();
|
|
429
|
+
this.idCounter = 0;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Get statistics about stored data
|
|
433
|
+
*/
|
|
434
|
+
getStats() {
|
|
435
|
+
return {
|
|
436
|
+
agents: this.agents.size,
|
|
437
|
+
threads: this.threads.size,
|
|
438
|
+
messages: Array.from(this.threads.values()).reduce(
|
|
439
|
+
(sum, thread) => sum + thread.messages.length,
|
|
440
|
+
0
|
|
441
|
+
)
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// src/storage/UpstashStorage.ts
|
|
447
|
+
var UpstashStorage = class {
|
|
448
|
+
constructor(config) {
|
|
449
|
+
this.url = config.url.replace(/\/$/, "");
|
|
450
|
+
this.token = config.token;
|
|
451
|
+
this.prefix = config.prefix || "snap-agent";
|
|
452
|
+
}
|
|
453
|
+
// ============================================================================
|
|
454
|
+
// Redis Commands via REST API
|
|
455
|
+
// ============================================================================
|
|
456
|
+
async command(cmd, ...args) {
|
|
457
|
+
const body = [cmd, ...args];
|
|
458
|
+
const response = await fetch(`${this.url}`, {
|
|
459
|
+
method: "POST",
|
|
460
|
+
headers: {
|
|
461
|
+
Authorization: `Bearer ${this.token}`,
|
|
462
|
+
"Content-Type": "application/json"
|
|
463
|
+
},
|
|
464
|
+
body: JSON.stringify(body)
|
|
465
|
+
});
|
|
466
|
+
if (!response.ok) {
|
|
467
|
+
const text = await response.text();
|
|
468
|
+
throw new Error(`Upstash Redis error: ${response.status} - ${text}`);
|
|
469
|
+
}
|
|
470
|
+
const data = await response.json();
|
|
471
|
+
if (data.error) {
|
|
472
|
+
throw new Error(`Upstash Redis error: ${data.error}`);
|
|
473
|
+
}
|
|
474
|
+
return data.result;
|
|
475
|
+
}
|
|
476
|
+
async pipeline(commands) {
|
|
477
|
+
const response = await fetch(`${this.url}/pipeline`, {
|
|
478
|
+
method: "POST",
|
|
479
|
+
headers: {
|
|
480
|
+
Authorization: `Bearer ${this.token}`,
|
|
481
|
+
"Content-Type": "application/json"
|
|
482
|
+
},
|
|
483
|
+
body: JSON.stringify(commands)
|
|
484
|
+
});
|
|
485
|
+
if (!response.ok) {
|
|
486
|
+
const text = await response.text();
|
|
487
|
+
throw new Error(`Upstash Redis pipeline error: ${response.status} - ${text}`);
|
|
488
|
+
}
|
|
489
|
+
const data = await response.json();
|
|
490
|
+
return data.map((item) => {
|
|
491
|
+
if (item.error) {
|
|
492
|
+
throw new Error(`Upstash Redis error: ${item.error}`);
|
|
493
|
+
}
|
|
494
|
+
return item.result;
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
// ============================================================================
|
|
498
|
+
// Key Generation
|
|
499
|
+
// ============================================================================
|
|
500
|
+
key(...parts) {
|
|
501
|
+
return `${this.prefix}:${parts.join(":")}`;
|
|
502
|
+
}
|
|
503
|
+
generateId() {
|
|
504
|
+
const timestamp = Date.now().toString(36);
|
|
505
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
506
|
+
return `${timestamp}${random}`;
|
|
507
|
+
}
|
|
508
|
+
// ============================================================================
|
|
509
|
+
// Agent Operations
|
|
510
|
+
// ============================================================================
|
|
511
|
+
async createAgent(config) {
|
|
512
|
+
const id = this.generateId();
|
|
513
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
514
|
+
const stored = {
|
|
515
|
+
id,
|
|
516
|
+
organizationId: config.organizationId,
|
|
517
|
+
userId: config.userId,
|
|
518
|
+
phone: config.phone,
|
|
519
|
+
name: config.name,
|
|
520
|
+
description: config.description,
|
|
521
|
+
instructions: config.instructions,
|
|
522
|
+
provider: config.provider,
|
|
523
|
+
model: config.model,
|
|
524
|
+
createdAt: now,
|
|
525
|
+
updatedAt: now,
|
|
526
|
+
files: JSON.stringify([]),
|
|
527
|
+
metadata: config.metadata ? JSON.stringify(config.metadata) : void 0
|
|
528
|
+
};
|
|
529
|
+
const fields = [];
|
|
530
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
531
|
+
if (value !== void 0) {
|
|
532
|
+
fields.push(key, String(value));
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
await this.pipeline([
|
|
536
|
+
["HSET", this.key("agent", id), ...fields],
|
|
537
|
+
["SADD", this.key("agents:user", config.userId), id],
|
|
538
|
+
...config.organizationId ? [["SADD", this.key("agents:org", config.organizationId), id]] : []
|
|
539
|
+
]);
|
|
540
|
+
return id;
|
|
541
|
+
}
|
|
542
|
+
async getAgent(agentId) {
|
|
543
|
+
const data = await this.command(
|
|
544
|
+
"HGETALL",
|
|
545
|
+
this.key("agent", agentId)
|
|
546
|
+
);
|
|
547
|
+
if (!data || Object.keys(data).length === 0) {
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
return this.parseStoredAgent(data);
|
|
551
|
+
}
|
|
552
|
+
async updateAgent(agentId, updates) {
|
|
553
|
+
const fields = ["updatedAt", (/* @__PURE__ */ new Date()).toISOString()];
|
|
554
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
555
|
+
if (value !== void 0) {
|
|
556
|
+
if (key === "metadata") {
|
|
557
|
+
fields.push(key, JSON.stringify(value));
|
|
558
|
+
} else {
|
|
559
|
+
fields.push(key, String(value));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
await this.command("HSET", this.key("agent", agentId), ...fields);
|
|
564
|
+
}
|
|
565
|
+
async deleteAgent(agentId) {
|
|
566
|
+
const agent = await this.getAgent(agentId);
|
|
567
|
+
if (!agent) return;
|
|
568
|
+
const threadIds = await this.command(
|
|
569
|
+
"SMEMBERS",
|
|
570
|
+
this.key("threads:agent", agentId)
|
|
571
|
+
);
|
|
572
|
+
const commands = [
|
|
573
|
+
["DEL", this.key("agent", agentId)],
|
|
574
|
+
["SREM", this.key("agents:user", agent.userId), agentId]
|
|
575
|
+
];
|
|
576
|
+
if (agent.organizationId) {
|
|
577
|
+
commands.push(["SREM", this.key("agents:org", agent.organizationId), agentId]);
|
|
578
|
+
}
|
|
579
|
+
for (const threadId of threadIds || []) {
|
|
580
|
+
commands.push(["DEL", this.key("thread", threadId)]);
|
|
581
|
+
}
|
|
582
|
+
commands.push(["DEL", this.key("threads:agent", agentId)]);
|
|
583
|
+
await this.pipeline(commands);
|
|
584
|
+
}
|
|
585
|
+
async listAgents(userId, organizationId) {
|
|
586
|
+
const indexKey = organizationId ? this.key("agents:org", organizationId) : this.key("agents:user", userId);
|
|
587
|
+
const agentIds = await this.command("SMEMBERS", indexKey);
|
|
588
|
+
if (!agentIds || agentIds.length === 0) {
|
|
589
|
+
return [];
|
|
590
|
+
}
|
|
591
|
+
const agents = [];
|
|
592
|
+
for (const id of agentIds) {
|
|
593
|
+
const agent = await this.getAgent(id);
|
|
594
|
+
if (agent) {
|
|
595
|
+
if (!organizationId || agent.userId === userId) {
|
|
596
|
+
agents.push(agent);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return agents.sort(
|
|
601
|
+
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
// ============================================================================
|
|
605
|
+
// Thread Operations
|
|
606
|
+
// ============================================================================
|
|
607
|
+
async createThread(config) {
|
|
608
|
+
const id = this.generateId();
|
|
609
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
610
|
+
const stored = {
|
|
611
|
+
id,
|
|
612
|
+
organizationId: config.organizationId,
|
|
613
|
+
agentId: config.agentId,
|
|
614
|
+
userId: config.userId,
|
|
615
|
+
endUserId: config.endUserId,
|
|
616
|
+
name: config.name,
|
|
617
|
+
createdAt: now,
|
|
618
|
+
updatedAt: now,
|
|
619
|
+
messages: JSON.stringify([]),
|
|
620
|
+
isPendingThread: "true",
|
|
621
|
+
metadata: config.metadata ? JSON.stringify(config.metadata) : void 0
|
|
622
|
+
};
|
|
623
|
+
const fields = [];
|
|
624
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
625
|
+
if (value !== void 0) {
|
|
626
|
+
fields.push(key, String(value));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
await this.pipeline([
|
|
630
|
+
["HSET", this.key("thread", id), ...fields],
|
|
631
|
+
["SADD", this.key("threads:agent", config.agentId), id],
|
|
632
|
+
["SADD", this.key("threads:user", config.userId), id],
|
|
633
|
+
...config.organizationId ? [["SADD", this.key("threads:org", config.organizationId), id]] : []
|
|
634
|
+
]);
|
|
635
|
+
return id;
|
|
636
|
+
}
|
|
637
|
+
async getThread(threadId) {
|
|
638
|
+
const data = await this.command(
|
|
639
|
+
"HGETALL",
|
|
640
|
+
this.key("thread", threadId)
|
|
641
|
+
);
|
|
642
|
+
if (!data || Object.keys(data).length === 0) {
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
return this.parseStoredThread(data);
|
|
646
|
+
}
|
|
647
|
+
async updateThread(threadId, updates) {
|
|
648
|
+
const fields = ["updatedAt", (/* @__PURE__ */ new Date()).toISOString()];
|
|
649
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
650
|
+
if (value !== void 0) {
|
|
651
|
+
if (key === "metadata") {
|
|
652
|
+
fields.push(key, JSON.stringify(value));
|
|
653
|
+
} else {
|
|
654
|
+
fields.push(key, String(value));
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
await this.command("HSET", this.key("thread", threadId), ...fields);
|
|
659
|
+
}
|
|
660
|
+
async deleteThread(threadId) {
|
|
661
|
+
const thread = await this.getThread(threadId);
|
|
662
|
+
if (!thread) return;
|
|
663
|
+
const commands = [
|
|
664
|
+
["DEL", this.key("thread", threadId)],
|
|
665
|
+
["SREM", this.key("threads:agent", thread.agentId), threadId],
|
|
666
|
+
["SREM", this.key("threads:user", thread.userId), threadId]
|
|
667
|
+
];
|
|
668
|
+
if (thread.organizationId) {
|
|
669
|
+
commands.push(["SREM", this.key("threads:org", thread.organizationId), threadId]);
|
|
670
|
+
}
|
|
671
|
+
await this.pipeline(commands);
|
|
672
|
+
}
|
|
673
|
+
async listThreads(filters) {
|
|
674
|
+
let indexKey;
|
|
675
|
+
if (filters.agentId) {
|
|
676
|
+
indexKey = this.key("threads:agent", filters.agentId);
|
|
677
|
+
} else if (filters.organizationId) {
|
|
678
|
+
indexKey = this.key("threads:org", filters.organizationId);
|
|
679
|
+
} else if (filters.userId) {
|
|
680
|
+
indexKey = this.key("threads:user", filters.userId);
|
|
681
|
+
} else {
|
|
682
|
+
return [];
|
|
683
|
+
}
|
|
684
|
+
const threadIds = await this.command("SMEMBERS", indexKey);
|
|
685
|
+
if (!threadIds || threadIds.length === 0) {
|
|
686
|
+
return [];
|
|
687
|
+
}
|
|
688
|
+
const threads = [];
|
|
689
|
+
for (const id of threadIds) {
|
|
690
|
+
const thread = await this.getThread(id);
|
|
691
|
+
if (thread) {
|
|
692
|
+
if (filters.userId && thread.userId !== filters.userId) continue;
|
|
693
|
+
if (filters.agentId && thread.agentId !== filters.agentId) continue;
|
|
694
|
+
if (filters.organizationId && thread.organizationId !== filters.organizationId) continue;
|
|
695
|
+
threads.push(thread);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return threads.sort(
|
|
699
|
+
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
// ============================================================================
|
|
703
|
+
// Message Operations
|
|
704
|
+
// ============================================================================
|
|
705
|
+
async addMessage(threadId, role, content, attachments) {
|
|
706
|
+
const thread = await this.getThread(threadId);
|
|
707
|
+
if (!thread) {
|
|
708
|
+
throw new Error(`Thread not found: ${threadId}`);
|
|
709
|
+
}
|
|
710
|
+
const messageId = this.generateId();
|
|
711
|
+
const message = {
|
|
712
|
+
id: messageId,
|
|
713
|
+
role,
|
|
714
|
+
content,
|
|
715
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
716
|
+
attachments
|
|
717
|
+
};
|
|
718
|
+
thread.messages.push(message);
|
|
719
|
+
await this.command(
|
|
720
|
+
"HSET",
|
|
721
|
+
this.key("thread", threadId),
|
|
722
|
+
"messages",
|
|
723
|
+
JSON.stringify(thread.messages),
|
|
724
|
+
"updatedAt",
|
|
725
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
726
|
+
"isPendingThread",
|
|
727
|
+
"false"
|
|
728
|
+
);
|
|
729
|
+
return messageId;
|
|
730
|
+
}
|
|
731
|
+
async getMessages(threadId, limit) {
|
|
732
|
+
const thread = await this.getThread(threadId);
|
|
733
|
+
if (!thread) return [];
|
|
734
|
+
const messages = [...thread.messages];
|
|
735
|
+
if (limit) {
|
|
736
|
+
return messages.slice(-limit);
|
|
737
|
+
}
|
|
738
|
+
return messages;
|
|
739
|
+
}
|
|
740
|
+
async getConversationContext(threadId, maxMessages = 20) {
|
|
741
|
+
const messages = await this.getMessages(threadId, maxMessages);
|
|
742
|
+
return messages.map((msg) => ({
|
|
743
|
+
role: msg.role,
|
|
744
|
+
content: msg.content
|
|
745
|
+
}));
|
|
746
|
+
}
|
|
747
|
+
// ============================================================================
|
|
748
|
+
// Helper Methods
|
|
749
|
+
// ============================================================================
|
|
750
|
+
parseStoredAgent(stored) {
|
|
751
|
+
return {
|
|
752
|
+
id: stored.id,
|
|
753
|
+
organizationId: stored.organizationId,
|
|
754
|
+
userId: stored.userId,
|
|
755
|
+
phone: stored.phone,
|
|
756
|
+
name: stored.name,
|
|
757
|
+
description: stored.description,
|
|
758
|
+
instructions: stored.instructions,
|
|
759
|
+
provider: stored.provider,
|
|
760
|
+
model: stored.model,
|
|
761
|
+
createdAt: new Date(stored.createdAt),
|
|
762
|
+
updatedAt: new Date(stored.updatedAt),
|
|
763
|
+
files: stored.files ? JSON.parse(stored.files) : [],
|
|
764
|
+
metadata: stored.metadata ? JSON.parse(stored.metadata) : void 0
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
parseStoredThread(stored) {
|
|
768
|
+
const messages = stored.messages ? JSON.parse(stored.messages) : [];
|
|
769
|
+
const parsedMessages = messages.map((msg) => ({
|
|
770
|
+
...msg,
|
|
771
|
+
timestamp: new Date(msg.timestamp)
|
|
772
|
+
}));
|
|
773
|
+
return {
|
|
774
|
+
id: stored.id,
|
|
775
|
+
organizationId: stored.organizationId,
|
|
776
|
+
agentId: stored.agentId,
|
|
777
|
+
userId: stored.userId,
|
|
778
|
+
endUserId: stored.endUserId,
|
|
779
|
+
name: stored.name,
|
|
780
|
+
createdAt: new Date(stored.createdAt),
|
|
781
|
+
updatedAt: new Date(stored.updatedAt),
|
|
782
|
+
messages: parsedMessages,
|
|
783
|
+
isPendingThread: stored.isPendingThread === "true",
|
|
784
|
+
metadata: stored.metadata ? JSON.parse(stored.metadata) : void 0
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
// ============================================================================
|
|
788
|
+
// Utility Methods
|
|
789
|
+
// ============================================================================
|
|
790
|
+
/**
|
|
791
|
+
* Test connection to Upstash Redis
|
|
792
|
+
*/
|
|
793
|
+
async ping() {
|
|
794
|
+
try {
|
|
795
|
+
const result = await this.command("PING");
|
|
796
|
+
return result === "PONG";
|
|
797
|
+
} catch {
|
|
798
|
+
return false;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Clear all data with this prefix (use with caution!)
|
|
803
|
+
*/
|
|
804
|
+
async clear() {
|
|
805
|
+
const keys = await this.command("KEYS", `${this.prefix}:*`);
|
|
806
|
+
if (keys && keys.length > 0) {
|
|
807
|
+
await this.command("DEL", ...keys);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Get storage statistics
|
|
812
|
+
*/
|
|
813
|
+
async getStats() {
|
|
814
|
+
const [agentKeys, threadKeys] = await this.pipeline([
|
|
815
|
+
["KEYS", `${this.prefix}:agent:*`],
|
|
816
|
+
["KEYS", `${this.prefix}:thread:*`]
|
|
817
|
+
]);
|
|
818
|
+
return {
|
|
819
|
+
agents: agentKeys?.length || 0,
|
|
820
|
+
threads: threadKeys?.length || 0
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
825
|
+
0 && (module.exports = {
|
|
826
|
+
MemoryStorage,
|
|
827
|
+
MongoDBStorage,
|
|
828
|
+
UpstashStorage
|
|
829
|
+
});
|