@ainetwork/adk-provider-memory-mongodb 0.4.0 → 0.5.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.
Files changed (54) hide show
  1. package/dist/{chunk-GPOFS7ZT.js → chunk-2XJ6S2W5.js} +12 -7
  2. package/dist/chunk-2XJ6S2W5.js.map +1 -0
  3. package/dist/{chunk-5CCEN7NK.js → chunk-QULDFKGZ.js} +5 -1
  4. package/dist/chunk-QULDFKGZ.js.map +1 -0
  5. package/dist/chunk-RC275GLE.js +70 -0
  6. package/dist/chunk-RC275GLE.js.map +1 -0
  7. package/dist/{chunk-NGLXQZLX.js → chunk-SJ2FHHN6.js} +13 -10
  8. package/dist/chunk-SJ2FHHN6.js.map +1 -0
  9. package/dist/index.cjs +322 -75
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +11 -3
  12. package/dist/index.d.ts +11 -3
  13. package/dist/index.js +232 -59
  14. package/dist/index.js.map +1 -1
  15. package/dist/models/messages.model.cjs +4 -0
  16. package/dist/models/messages.model.cjs.map +1 -1
  17. package/dist/models/messages.model.d.cts +14 -1
  18. package/dist/models/messages.model.d.ts +14 -1
  19. package/dist/models/messages.model.js +1 -1
  20. package/dist/models/threads.model.cjs +11 -6
  21. package/dist/models/threads.model.cjs.map +1 -1
  22. package/dist/models/threads.model.d.cts +22 -9
  23. package/dist/models/threads.model.d.ts +22 -9
  24. package/dist/models/threads.model.js +1 -1
  25. package/dist/models/user-workflow.model.cjs +105 -0
  26. package/dist/models/user-workflow.model.cjs.map +1 -0
  27. package/dist/models/user-workflow.model.d.cts +92 -0
  28. package/dist/models/user-workflow.model.d.ts +92 -0
  29. package/dist/models/user-workflow.model.js +9 -0
  30. package/dist/models/{workflow.model.cjs → workflow-template.model.cjs} +18 -15
  31. package/dist/models/workflow-template.model.cjs.map +1 -0
  32. package/dist/models/workflow-template.model.d.cts +60 -0
  33. package/dist/models/workflow-template.model.d.ts +60 -0
  34. package/dist/models/workflow-template.model.js +9 -0
  35. package/dist/models/workflow-template.model.js.map +1 -0
  36. package/implements/base.memory.ts +130 -12
  37. package/implements/thread.memory.ts +48 -22
  38. package/implements/user-workflow.memory.ts +87 -0
  39. package/implements/workflow-template.memory.ts +69 -0
  40. package/models/messages.model.ts +7 -0
  41. package/models/threads.model.ts +16 -9
  42. package/models/user-workflow.model.ts +91 -0
  43. package/models/workflow-template.model.ts +58 -0
  44. package/package.json +3 -3
  45. package/dist/chunk-5CCEN7NK.js.map +0 -1
  46. package/dist/chunk-GPOFS7ZT.js.map +0 -1
  47. package/dist/chunk-NGLXQZLX.js.map +0 -1
  48. package/dist/models/workflow.model.cjs.map +0 -1
  49. package/dist/models/workflow.model.d.cts +0 -54
  50. package/dist/models/workflow.model.d.ts +0 -54
  51. package/dist/models/workflow.model.js +0 -9
  52. package/implements/workflow.memory.ts +0 -78
  53. package/models/workflow.model.ts +0 -46
  54. /package/dist/models/{workflow.model.js.map → user-workflow.model.js.map} +0 -0
@@ -0,0 +1,60 @@
1
+ import mongoose, { Schema, Document } from 'mongoose';
2
+
3
+ declare const WorkflowTemplateObjectSchema: Schema<any, mongoose.Model<any, any, any, any, any, any>, {}, {}, {}, {}, {
4
+ timestamps: true;
5
+ }, {
6
+ createdAt: NativeDate;
7
+ updatedAt: NativeDate;
8
+ } & {
9
+ description: string;
10
+ title: string;
11
+ content: string;
12
+ active: boolean;
13
+ templateId: string;
14
+ variables?: any;
15
+ }, Document<unknown, {}, mongoose.FlatRecord<{
16
+ createdAt: NativeDate;
17
+ updatedAt: NativeDate;
18
+ } & {
19
+ description: string;
20
+ title: string;
21
+ content: string;
22
+ active: boolean;
23
+ templateId: string;
24
+ variables?: any;
25
+ }>, {}> & mongoose.FlatRecord<{
26
+ createdAt: NativeDate;
27
+ updatedAt: NativeDate;
28
+ } & {
29
+ description: string;
30
+ title: string;
31
+ content: string;
32
+ active: boolean;
33
+ templateId: string;
34
+ variables?: any;
35
+ }> & {
36
+ _id: mongoose.Types.ObjectId;
37
+ } & {
38
+ __v: number;
39
+ }>;
40
+ interface WorkflowTemplateDocument extends Document {
41
+ templateId: string;
42
+ title: string;
43
+ description: string;
44
+ active: boolean;
45
+ content: string;
46
+ variables?: Record<string, {
47
+ id: string;
48
+ label: string;
49
+ type: "select" | "date_range" | "date_parts" | "text" | "number";
50
+ options?: Array<string>;
51
+ resolveAt?: "creation" | "execution";
52
+ }>;
53
+ }
54
+ declare const WorkflowTemplateModel: mongoose.Model<WorkflowTemplateDocument, {}, {}, {}, Document<unknown, {}, WorkflowTemplateDocument, {}> & WorkflowTemplateDocument & Required<{
55
+ _id: unknown;
56
+ }> & {
57
+ __v: number;
58
+ }, any>;
59
+
60
+ export { type WorkflowTemplateDocument, WorkflowTemplateModel, WorkflowTemplateObjectSchema };
@@ -0,0 +1,60 @@
1
+ import mongoose, { Schema, Document } from 'mongoose';
2
+
3
+ declare const WorkflowTemplateObjectSchema: Schema<any, mongoose.Model<any, any, any, any, any, any>, {}, {}, {}, {}, {
4
+ timestamps: true;
5
+ }, {
6
+ createdAt: NativeDate;
7
+ updatedAt: NativeDate;
8
+ } & {
9
+ description: string;
10
+ title: string;
11
+ content: string;
12
+ active: boolean;
13
+ templateId: string;
14
+ variables?: any;
15
+ }, Document<unknown, {}, mongoose.FlatRecord<{
16
+ createdAt: NativeDate;
17
+ updatedAt: NativeDate;
18
+ } & {
19
+ description: string;
20
+ title: string;
21
+ content: string;
22
+ active: boolean;
23
+ templateId: string;
24
+ variables?: any;
25
+ }>, {}> & mongoose.FlatRecord<{
26
+ createdAt: NativeDate;
27
+ updatedAt: NativeDate;
28
+ } & {
29
+ description: string;
30
+ title: string;
31
+ content: string;
32
+ active: boolean;
33
+ templateId: string;
34
+ variables?: any;
35
+ }> & {
36
+ _id: mongoose.Types.ObjectId;
37
+ } & {
38
+ __v: number;
39
+ }>;
40
+ interface WorkflowTemplateDocument extends Document {
41
+ templateId: string;
42
+ title: string;
43
+ description: string;
44
+ active: boolean;
45
+ content: string;
46
+ variables?: Record<string, {
47
+ id: string;
48
+ label: string;
49
+ type: "select" | "date_range" | "date_parts" | "text" | "number";
50
+ options?: Array<string>;
51
+ resolveAt?: "creation" | "execution";
52
+ }>;
53
+ }
54
+ declare const WorkflowTemplateModel: mongoose.Model<WorkflowTemplateDocument, {}, {}, {}, Document<unknown, {}, WorkflowTemplateDocument, {}> & WorkflowTemplateDocument & Required<{
55
+ _id: unknown;
56
+ }> & {
57
+ __v: number;
58
+ }, any>;
59
+
60
+ export { type WorkflowTemplateDocument, WorkflowTemplateModel, WorkflowTemplateObjectSchema };
@@ -0,0 +1,9 @@
1
+ import {
2
+ WorkflowTemplateModel,
3
+ WorkflowTemplateObjectSchema
4
+ } from "../chunk-SJ2FHHN6.js";
5
+ export {
6
+ WorkflowTemplateModel,
7
+ WorkflowTemplateObjectSchema
8
+ };
9
+ //# sourceMappingURL=workflow-template.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,10 +1,12 @@
1
- import { IAgentMemory, IIntentMemory, IMemory, IThreadMemory, IWorkflowMemory } from "@ainetwork/adk/modules";
1
+ import { IAgentMemory, IIntentMemory, IMemory, IUserWorkflowMemory, IThreadMemory, IWorkflowTemplateMemory } from "@ainetwork/adk/modules";
2
2
  import mongoose from "mongoose";
3
3
  import { loggers } from "@ainetwork/adk/utils/logger";
4
4
  import { MongoDBAgent } from "./agent.memory";
5
5
  import { MongoDBIntent } from "./intent.memory";
6
6
  import { MongoDBThread } from "./thread.memory";
7
- import { MongoDBWorkflow } from "./workflow.memory";
7
+ import { MongoDBUserWorkflow } from "./user-workflow.memory";
8
+ import { MongoDBWorkflowTemplate } from "./workflow-template.memory";
9
+ import { MessageModel } from "../models/messages.model";
8
10
 
9
11
  export interface MongoDBMemoryConfig {
10
12
  uri: string;
@@ -15,6 +17,7 @@ export interface MongoDBMemoryConfig {
15
17
  socketTimeoutMS?: number;
16
18
  connectTimeoutMS?: number;
17
19
  operationTimeoutMS?: number; // Timeout for database operations
20
+ threadTTLSeconds?: number; // TTL for thread documents (in seconds). Orphaned messages are periodically cleaned up.
18
21
  }
19
22
 
20
23
  export class MongoDBMemory implements IMemory {
@@ -28,11 +31,14 @@ export class MongoDBMemory implements IMemory {
28
31
  private connectionConfig: mongoose.ConnectOptions;
29
32
  private eventListenersSetup: boolean = false;
30
33
  private operationTimeoutMS: number;
34
+ private threadTTLSeconds?: number;
35
+ private orphanCleanupTimer?: ReturnType<typeof setInterval>;
31
36
 
32
37
  private agentMemory: MongoDBAgent;
33
38
  private intentMemory: MongoDBIntent;
34
39
  private threadMemory: MongoDBThread;
35
- private workflowMemory: MongoDBWorkflow;
40
+ private workflowTemplateMemory: MongoDBWorkflowTemplate;
41
+ private userWorkflowMemory: MongoDBUserWorkflow;
36
42
 
37
43
  constructor(config: string | MongoDBMemoryConfig) {
38
44
  const cfg = typeof config === 'string' ? { uri: config } : config;
@@ -41,8 +47,13 @@ export class MongoDBMemory implements IMemory {
41
47
  this.maxReconnectAttempts = cfg.maxReconnectAttempts ?? 5;
42
48
  this.reconnectInterval = cfg.reconnectInterval ?? 5000;
43
49
  this.operationTimeoutMS = cfg.operationTimeoutMS ?? 10000; // Default 10 seconds
50
+ if (cfg.threadTTLSeconds !== undefined && cfg.threadTTLSeconds > 0) {
51
+ this.threadTTLSeconds = cfg.threadTTLSeconds;
52
+ }
44
53
  this.connectionConfig = {
45
- maxPoolSize: cfg.maxPoolSize ?? 1,
54
+ maxPoolSize: cfg.maxPoolSize ?? 10,
55
+ minPoolSize: 0,
56
+ maxIdleTimeMS: 30000,
46
57
  serverSelectionTimeoutMS: cfg.serverSelectionTimeoutMS ?? 30000,
47
58
  socketTimeoutMS: cfg.socketTimeoutMS ?? 45000,
48
59
  connectTimeoutMS: cfg.connectTimeoutMS ?? 30000,
@@ -73,7 +84,12 @@ export class MongoDBMemory implements IMemory {
73
84
  this.getOperationTimeout.bind(this)
74
85
  );
75
86
 
76
- this.workflowMemory = new MongoDBWorkflow(
87
+ this.workflowTemplateMemory = new MongoDBWorkflowTemplate(
88
+ this.executeWithRetry.bind(this),
89
+ this.getOperationTimeout.bind(this)
90
+ );
91
+
92
+ this.userWorkflowMemory = new MongoDBUserWorkflow(
77
93
  this.executeWithRetry.bind(this),
78
94
  this.getOperationTimeout.bind(this)
79
95
  );
@@ -91,8 +107,12 @@ export class MongoDBMemory implements IMemory {
91
107
  return this.intentMemory;
92
108
  }
93
109
 
94
- public getWorkflowMemory(): IWorkflowMemory {
95
- return this.workflowMemory;
110
+ public getWorkflowTemplateMemory(): IWorkflowTemplateMemory {
111
+ return this.workflowTemplateMemory;
112
+ }
113
+
114
+ public getUserWorkflowMemory(): IUserWorkflowMemory {
115
+ return this.userWorkflowMemory;
96
116
  }
97
117
 
98
118
  private setupMongooseEventListeners(): void {
@@ -134,7 +154,7 @@ export class MongoDBMemory implements IMemory {
134
154
 
135
155
  this.reconnecting = true;
136
156
 
137
- while (this.reconnectAttempts < this.maxReconnectAttempts && !this.isConnected) {
157
+ while (this.reconnectAttempts < this.maxReconnectAttempts && !this.connected) {
138
158
  this.reconnectAttempts++;
139
159
  loggers.agent.info(
140
160
  `Attempting to reconnect to MongoDB (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`
@@ -163,7 +183,7 @@ export class MongoDBMemory implements IMemory {
163
183
 
164
184
  this.reconnecting = false;
165
185
 
166
- if (!this.isConnected) {
186
+ if (!this.connected) {
167
187
  loggers.agent.error(
168
188
  `Failed to reconnect to MongoDB after ${this.maxReconnectAttempts} attempts`
169
189
  );
@@ -179,6 +199,8 @@ export class MongoDBMemory implements IMemory {
179
199
  await mongoose.connect(this.uri, this.connectionConfig);
180
200
  this.connected = true;
181
201
  this.reconnectAttempts = 0;
202
+ await this.setupTTLIndex();
203
+ this.startOrphanCleanup();
182
204
  } catch (error) {
183
205
  loggers.agent.error("Failed to connect to MongoDB:", error);
184
206
  throw error;
@@ -186,11 +208,15 @@ export class MongoDBMemory implements IMemory {
186
208
  }
187
209
 
188
210
  public async disconnect(): Promise<void> {
189
- if (!this.isConnected) {
211
+ if (!this.connected) {
190
212
  return;
191
213
  }
192
214
 
193
215
  try {
216
+ if (this.orphanCleanupTimer) {
217
+ clearInterval(this.orphanCleanupTimer);
218
+ this.orphanCleanupTimer = undefined;
219
+ }
194
220
  await mongoose.disconnect();
195
221
  this.connected = false;
196
222
  } catch (error) {
@@ -204,7 +230,7 @@ export class MongoDBMemory implements IMemory {
204
230
  }
205
231
 
206
232
  private async ensureConnection(): Promise<void> {
207
- if (!this.isConnected && !this.reconnecting) {
233
+ if (!this.connected && !this.reconnecting) {
208
234
  await this.connect();
209
235
  }
210
236
 
@@ -215,11 +241,79 @@ export class MongoDBMemory implements IMemory {
215
241
  await new Promise((resolve) => setTimeout(resolve, 100));
216
242
  }
217
243
 
218
- if (!this.isConnected) {
244
+ if (!this.connected) {
219
245
  throw new Error("MongoDB is not connected and reconnection failed");
220
246
  }
221
247
  }
222
248
 
249
+ private async setupTTLIndex(): Promise<void> {
250
+ if (this.threadTTLSeconds === undefined) return;
251
+
252
+ try {
253
+ const db = mongoose.connection.db;
254
+ if (!db) return;
255
+
256
+ const collection = db.collection('threads');
257
+ const indexes = await collection.indexes();
258
+ const existingTTL = indexes.find(
259
+ (idx) => idx.key?.updatedAt !== undefined && idx.expireAfterSeconds !== undefined
260
+ );
261
+
262
+ if (existingTTL) {
263
+ if (existingTTL.expireAfterSeconds !== this.threadTTLSeconds) {
264
+ await db.command({
265
+ collMod: 'threads',
266
+ index: { keyPattern: { updatedAt: 1 }, expireAfterSeconds: this.threadTTLSeconds },
267
+ });
268
+ loggers.agent.info(`Thread TTL index updated to ${this.threadTTLSeconds} seconds`);
269
+ }
270
+ } else {
271
+ await collection.createIndex(
272
+ { updatedAt: 1 },
273
+ { expireAfterSeconds: this.threadTTLSeconds },
274
+ );
275
+ loggers.agent.info(`Thread TTL index created with ${this.threadTTLSeconds} seconds`);
276
+ }
277
+ } catch (error) {
278
+ loggers.agent.error('Failed to setup TTL index:', error);
279
+ }
280
+ }
281
+
282
+ private startOrphanCleanup(): void {
283
+ if (this.threadTTLSeconds === undefined) return;
284
+ if (this.orphanCleanupTimer) return;
285
+
286
+ // Run cleanup at half the TTL interval, with a minimum of 60s and maximum of 1 hour
287
+ const intervalMs = Math.max(60_000, Math.min(this.threadTTLSeconds * 500, 3_600_000));
288
+ this.orphanCleanupTimer = setInterval(() => {
289
+ this.cleanupOrphanedMessages().catch((error) => {
290
+ loggers.agent.error('Orphaned message cleanup failed:', error);
291
+ });
292
+ }, intervalMs);
293
+
294
+ loggers.agent.info(`Orphaned message cleanup scheduled every ${Math.round(intervalMs / 1000)}s`);
295
+ }
296
+
297
+ private async cleanupOrphanedMessages(): Promise<void> {
298
+ if (!this.connected) return;
299
+
300
+ try {
301
+ const db = mongoose.connection.db;
302
+ if (!db) return;
303
+
304
+ const existingThreadIds = await db.collection('threads').distinct('threadId');
305
+ const result = await MessageModel.deleteMany({
306
+ threadId: { $nin: existingThreadIds },
307
+ });
308
+
309
+ if (result.deletedCount > 0) {
310
+ loggers.agent.info(`Cleaned up ${result.deletedCount} orphaned messages`);
311
+ }
312
+ } catch (error) {
313
+ loggers.agent.error('Failed to cleanup orphaned messages:', error);
314
+ }
315
+ }
316
+
223
317
  /**
224
318
  * Get the operation timeout in milliseconds
225
319
  */
@@ -246,6 +340,30 @@ export class MongoDBMemory implements IMemory {
246
340
  throw error;
247
341
  }
248
342
 
343
+ // Check if it's a TooManyLogicalSessions error
344
+ if (error.code === 261 || error.codeName === "TooManyLogicalSessions") {
345
+ loggers.agent.warn(
346
+ `${operationName} failed due to too many sessions, disconnecting to release sessions...`
347
+ );
348
+
349
+ try {
350
+ await mongoose.disconnect();
351
+ this.connected = false;
352
+ } catch (disconnectError) {
353
+ loggers.agent.error("Failed to disconnect during session cleanup:", disconnectError);
354
+ }
355
+
356
+ await new Promise((resolve) => setTimeout(resolve, 5000));
357
+ await this.ensureConnection();
358
+
359
+ try {
360
+ return await operation();
361
+ } catch (retryError: any) {
362
+ loggers.agent.error(`${operationName} failed after session cleanup retry:`, retryError);
363
+ throw retryError;
364
+ }
365
+ }
366
+
249
367
  // Check if it's a connection-related error
250
368
  if (
251
369
  error.name === "MongoNetworkError" ||
@@ -1,4 +1,4 @@
1
- import type { MessageObject, ThreadMetadata, ThreadObject, ThreadType } from "@ainetwork/adk/types/memory";
1
+ import type { MessageObject, ThreadMetadata, ThreadObject, ThreadType, ThreadFilter } from "@ainetwork/adk/types/memory";
2
2
  import { MessageRole } from "@ainetwork/adk/types/memory";
3
3
  import { IThreadMemory } from "@ainetwork/adk/modules";
4
4
  import { ThreadDocument, ThreadModel } from "../models/threads.model";
@@ -44,6 +44,8 @@ export class MongoDBThread implements IThreadMemory {
44
44
  userId: thread.userId,
45
45
  type: thread.type as ThreadType,
46
46
  title: thread.title || "New thread",
47
+ isPinned: thread.isPinned ?? false,
48
+ workflowId: thread.workflowId,
47
49
  messages: []
48
50
  };
49
51
  messages.forEach((message: MessageDocument) => {
@@ -65,19 +67,18 @@ export class MongoDBThread implements IThreadMemory {
65
67
  userId: string,
66
68
  threadId: string,
67
69
  title: string,
70
+ workflowId?: string,
68
71
  ): Promise<ThreadObject> {
69
72
  return this.executeWithRetry(async () => {
70
- const now = Date.now();
71
73
  await ThreadModel.create({
72
74
  type,
73
75
  userId,
74
76
  threadId,
75
77
  title,
76
- updated_at: now,
77
- created_at: now,
78
+ workflowId,
78
79
  });
79
80
 
80
- return { type, userId, threadId, title, messages: []};
81
+ return { type, userId, threadId, title, workflowId, messages: []};
81
82
  }, `createThread(${userId}, ${threadId})`);
82
83
  };
83
84
 
@@ -87,20 +88,25 @@ export class MongoDBThread implements IThreadMemory {
87
88
  messages: MessageObject[]
88
89
  ): Promise<void> {
89
90
  return this.executeWithRetry(async () => {
90
- await ThreadModel.updateOne({ threadId, userId }, {
91
- updated_at: Date.now(),
92
- });
93
- for (const message of messages) {
94
- await MessageModel.create({
95
- threadId,
96
- messageId: message.messageId,
97
- userId,
98
- role: message.role,
99
- content: message.content,
100
- timestamp: message.timestamp,
101
- metadata: message.metadata,
102
- });
91
+ if (messages.length > 0) {
92
+ const messageIds = messages.map((m) => m.messageId);
93
+ await MessageModel.deleteMany({ threadId, userId, messageId: { $in: messageIds } });
94
+ await MessageModel.insertMany(
95
+ messages.map((message) => ({
96
+ threadId,
97
+ messageId: message.messageId,
98
+ userId,
99
+ role: message.role,
100
+ content: message.content,
101
+ timestamp: message.timestamp,
102
+ metadata: message.metadata,
103
+ }))
104
+ );
103
105
  }
106
+ await ThreadModel.updateOne(
107
+ { threadId, userId },
108
+ { $set: { updatedAt: new Date() } }
109
+ );
104
110
  }, `addMessagesToThread(${userId}, ${threadId})`);
105
111
  };
106
112
 
@@ -116,11 +122,14 @@ export class MongoDBThread implements IThreadMemory {
116
122
  }, `deleteThread(${userId}, ${threadId})`);
117
123
  };
118
124
 
119
- public async listThreads(userId: string): Promise<ThreadMetadata[]> {
125
+ public async listThreads(userId: string, filter?: ThreadFilter): Promise<ThreadMetadata[]> {
120
126
  return this.executeWithRetry(async () => {
121
127
  const timeout = this.getOperationTimeout();
122
- const threads = await ThreadModel.find({ userId })
123
- .sort({ updated_at: -1 })
128
+ const query: Record<string, any> = { userId };
129
+ if (filter?.workflowId) query.workflowId = filter.workflowId;
130
+ if (filter?.type) query.type = filter.type;
131
+ const threads = await ThreadModel.find(query)
132
+ .sort({ updatedAt: -1 })
124
133
  .maxTimeMS(timeout);
125
134
  const data: ThreadMetadata[] = threads.map((thread: ThreadDocument) => {
126
135
  return {
@@ -128,10 +137,27 @@ export class MongoDBThread implements IThreadMemory {
128
137
  userId,
129
138
  threadId: thread.threadId,
130
139
  title: thread.title,
131
- updatedAt: thread.updated_at
140
+ isPinned: thread.isPinned ?? false,
141
+ workflowId: thread.workflowId,
142
+ createdAt: thread.createdAt?.toISOString(),
143
+ updatedAt: thread.updatedAt?.toISOString(),
132
144
  } as ThreadMetadata;
133
145
  })
134
146
  return data;
135
147
  }, `listThreads(${userId})`);
136
148
  };
149
+
150
+ public async updateThreadPin(
151
+ userId: string,
152
+ threadId: string,
153
+ isPinned: boolean
154
+ ): Promise<void> {
155
+ return this.executeWithRetry(async () => {
156
+ const timeout = this.getOperationTimeout();
157
+ await ThreadModel.updateOne(
158
+ { threadId, userId },
159
+ { $set: { isPinned } }
160
+ ).maxTimeMS(timeout);
161
+ }, `updateThreadPin(${userId}, ${threadId})`);
162
+ };
137
163
  }
@@ -0,0 +1,87 @@
1
+ import type { IUserWorkflowMemory } from "@ainetwork/adk/modules";
2
+ import type { UserWorkflow } from "@ainetwork/adk/types/memory";
3
+ import { UserWorkflowModel } from "../models/user-workflow.model";
4
+
5
+ export type ExecuteWithRetryFn = <T>(
6
+ operation: () => Promise<T>,
7
+ operationName?: string
8
+ ) => Promise<T>;
9
+
10
+ export type GetOperationTimeoutFn = () => number;
11
+
12
+ export class MongoDBUserWorkflow implements IUserWorkflowMemory {
13
+ private executeWithRetry: ExecuteWithRetryFn;
14
+ private getOperationTimeout: GetOperationTimeoutFn;
15
+
16
+ constructor(
17
+ executeWithRetry: ExecuteWithRetryFn,
18
+ getOperationTimeout: GetOperationTimeoutFn
19
+ ) {
20
+ this.executeWithRetry = executeWithRetry;
21
+ this.getOperationTimeout = getOperationTimeout;
22
+ }
23
+
24
+ public async createUserWorkflow(workflow: UserWorkflow): Promise<UserWorkflow> {
25
+ return this.executeWithRetry(async () => {
26
+ const created = await UserWorkflowModel.create(workflow);
27
+ return created.toObject() as UserWorkflow;
28
+ }, "createUserWorkflow()");
29
+ }
30
+
31
+ public async getUserWorkflow(workflowId: string): Promise<UserWorkflow | undefined> {
32
+ return this.executeWithRetry(async () => {
33
+ const timeout = this.getOperationTimeout();
34
+ const workflow = await UserWorkflowModel.findOne({ workflowId })
35
+ .maxTimeMS(timeout)
36
+ .lean<UserWorkflow>();
37
+ return workflow || undefined;
38
+ }, "getUserWorkflow()");
39
+ }
40
+
41
+ public async updateUserWorkflow(workflowId: string, updates: Partial<UserWorkflow>): Promise<void> {
42
+ if (!updates.userId) {
43
+ throw new Error("userId is required for updateUserWorkflow");
44
+ }
45
+
46
+ const { userId, workflowId: _workflowId, ...mutableUpdates } = updates;
47
+
48
+ return this.executeWithRetry(async () => {
49
+ const timeout = this.getOperationTimeout();
50
+ await UserWorkflowModel.updateOne(
51
+ { workflowId, userId },
52
+ { $set: mutableUpdates }
53
+ ).maxTimeMS(timeout);
54
+ }, "updateUserWorkflow()");
55
+ }
56
+
57
+ public async deleteUserWorkflow(workflowId: string, userId: string): Promise<void> {
58
+ return this.executeWithRetry(async () => {
59
+ const timeout = this.getOperationTimeout();
60
+ await UserWorkflowModel.deleteOne({ workflowId, userId }).maxTimeMS(timeout);
61
+ }, "deleteUserWorkflow()");
62
+ }
63
+
64
+ public async listUserWorkflows(userId?: string): Promise<UserWorkflow[]> {
65
+ return this.executeWithRetry(async () => {
66
+ const timeout = this.getOperationTimeout();
67
+ const query = userId ? { userId } : {};
68
+ const workflows = await UserWorkflowModel.find(query)
69
+ .maxTimeMS(timeout)
70
+ .lean<UserWorkflow[]>();
71
+ return workflows;
72
+ }, "listUserWorkflows()");
73
+ }
74
+
75
+ public async listActiveScheduledWorkflows(): Promise<UserWorkflow[]> {
76
+ return this.executeWithRetry(async () => {
77
+ const timeout = this.getOperationTimeout();
78
+ const workflows = await UserWorkflowModel.find({
79
+ active: true,
80
+ schedule: { $exists: true, $ne: null },
81
+ })
82
+ .maxTimeMS(timeout)
83
+ .lean<UserWorkflow[]>();
84
+ return workflows;
85
+ }, "listActiveScheduledWorkflows()");
86
+ }
87
+ }
@@ -0,0 +1,69 @@
1
+ import type { IWorkflowTemplateMemory } from "@ainetwork/adk/modules";
2
+ import type { WorkflowTemplate } from "@ainetwork/adk/types/memory";
3
+ import { WorkflowTemplateModel } from "../models/workflow-template.model";
4
+
5
+ export type ExecuteWithRetryFn = <T>(
6
+ operation: () => Promise<T>,
7
+ operationName?: string
8
+ ) => Promise<T>;
9
+
10
+ export type GetOperationTimeoutFn = () => number;
11
+
12
+ export class MongoDBWorkflowTemplate implements IWorkflowTemplateMemory {
13
+ private executeWithRetry: ExecuteWithRetryFn;
14
+ private getOperationTimeout: GetOperationTimeoutFn;
15
+
16
+ constructor(
17
+ executeWithRetry: ExecuteWithRetryFn,
18
+ getOperationTimeout: GetOperationTimeoutFn
19
+ ) {
20
+ this.executeWithRetry = executeWithRetry;
21
+ this.getOperationTimeout = getOperationTimeout;
22
+ }
23
+
24
+ public async createTemplate(template: WorkflowTemplate): Promise<WorkflowTemplate> {
25
+ return this.executeWithRetry(async () => {
26
+ const created = await WorkflowTemplateModel.create(template);
27
+ return created.toObject() as WorkflowTemplate;
28
+ }, "createTemplate()");
29
+ }
30
+
31
+ public async getTemplate(templateId: string): Promise<WorkflowTemplate | undefined> {
32
+ return this.executeWithRetry(async () => {
33
+ const timeout = this.getOperationTimeout();
34
+ const template = await WorkflowTemplateModel.findOne({ templateId })
35
+ .maxTimeMS(timeout)
36
+ .lean<WorkflowTemplate>();
37
+ return template || undefined;
38
+ }, "getTemplate()");
39
+ }
40
+
41
+ public async updateTemplate(templateId: string, updates: Partial<WorkflowTemplate>): Promise<void> {
42
+ const { templateId: _templateId, ...mutableUpdates } = updates;
43
+
44
+ return this.executeWithRetry(async () => {
45
+ const timeout = this.getOperationTimeout();
46
+ await WorkflowTemplateModel.updateOne(
47
+ { templateId },
48
+ { $set: mutableUpdates }
49
+ ).maxTimeMS(timeout);
50
+ }, "updateTemplate()");
51
+ }
52
+
53
+ public async deleteTemplate(templateId: string): Promise<void> {
54
+ return this.executeWithRetry(async () => {
55
+ const timeout = this.getOperationTimeout();
56
+ await WorkflowTemplateModel.deleteOne({ templateId }).maxTimeMS(timeout);
57
+ }, "deleteTemplate()");
58
+ }
59
+
60
+ public async listTemplates(): Promise<WorkflowTemplate[]> {
61
+ return this.executeWithRetry(async () => {
62
+ const timeout = this.getOperationTimeout();
63
+ const templates = await WorkflowTemplateModel.find()
64
+ .maxTimeMS(timeout)
65
+ .lean<WorkflowTemplate[]>();
66
+ return templates;
67
+ }, "listTemplates()");
68
+ }
69
+ }
@@ -47,8 +47,13 @@ export const MessageObjectSchema = new Schema(
47
47
  default: {},
48
48
  },
49
49
  },
50
+ {
51
+ timestamps: true,
52
+ },
50
53
  );
51
54
 
55
+ MessageObjectSchema.index({ threadId: 1, messageId: 1 }, { unique: true });
56
+
52
57
  // Message Document interface
53
58
  export interface MessageDocument extends Document {
54
59
  messageId: string;
@@ -60,6 +65,8 @@ export interface MessageDocument extends Document {
60
65
  parts: any[];
61
66
  };
62
67
  timestamp: number;
68
+ createdAt: Date;
69
+ updatedAt: Date;
63
70
  metadata?: { [key: string]: unknown };
64
71
  }
65
72