@librechat/data-schemas 0.0.35 → 0.0.37

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/dist/index.cjs CHANGED
@@ -1462,12 +1462,57 @@ const agentSchema = new mongoose.Schema({
1462
1462
  default: [],
1463
1463
  index: true,
1464
1464
  },
1465
+ /** Per-tool configuration (defer_loading, allowed_callers) */
1466
+ tool_options: {
1467
+ type: mongoose.Schema.Types.Mixed,
1468
+ default: undefined,
1469
+ },
1465
1470
  }, {
1466
1471
  timestamps: true,
1467
1472
  });
1468
1473
  agentSchema.index({ updatedAt: -1, _id: 1 });
1469
1474
  agentSchema.index({ 'edges.to': 1 });
1470
1475
 
1476
+ const agentApiKeySchema = new mongoose.Schema({
1477
+ userId: {
1478
+ type: mongoose.Schema.Types.ObjectId,
1479
+ ref: 'User',
1480
+ required: true,
1481
+ index: true,
1482
+ },
1483
+ name: {
1484
+ type: String,
1485
+ required: true,
1486
+ trim: true,
1487
+ maxlength: 100,
1488
+ },
1489
+ keyHash: {
1490
+ type: String,
1491
+ required: true,
1492
+ select: false,
1493
+ index: true,
1494
+ },
1495
+ keyPrefix: {
1496
+ type: String,
1497
+ required: true,
1498
+ index: true,
1499
+ },
1500
+ lastUsedAt: {
1501
+ type: Date,
1502
+ },
1503
+ expiresAt: {
1504
+ type: Date,
1505
+ },
1506
+ }, { timestamps: true });
1507
+ agentApiKeySchema.index({ userId: 1, name: 1 });
1508
+ /**
1509
+ * TTL index for automatic cleanup of expired keys.
1510
+ * MongoDB deletes documents when expiresAt passes (expireAfterSeconds: 0 means immediate).
1511
+ * Note: Expired keys are permanently removed, not soft-deleted.
1512
+ * If audit trails are needed, remove this index and check expiration programmatically.
1513
+ */
1514
+ agentApiKeySchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
1515
+
1471
1516
  const agentCategorySchema = new mongoose.Schema({
1472
1517
  value: {
1473
1518
  type: String,
@@ -1730,6 +1775,12 @@ const conversationPreset = {
1730
1775
  thinkingBudget: {
1731
1776
  type: Number,
1732
1777
  },
1778
+ thinkingLevel: {
1779
+ type: String,
1780
+ },
1781
+ effort: {
1782
+ type: String,
1783
+ },
1733
1784
  system: {
1734
1785
  type: String,
1735
1786
  },
@@ -1856,6 +1907,10 @@ const file = new mongoose.Schema({
1856
1907
  ref: 'Conversation',
1857
1908
  index: true,
1858
1909
  },
1910
+ messageId: {
1911
+ type: String,
1912
+ index: true,
1913
+ },
1859
1914
  file_id: {
1860
1915
  type: String,
1861
1916
  index: true,
@@ -1919,6 +1974,7 @@ const file = new mongoose.Schema({
1919
1974
  timestamps: true,
1920
1975
  });
1921
1976
  file.index({ createdAt: 1, updatedAt: 1 });
1977
+ file.index({ filename: 1, conversationId: 1, context: 1 }, { unique: true, partialFilterExpression: { context: librechatDataProvider.FileContext.execute_code } });
1922
1978
 
1923
1979
  const keySchema = new mongoose.Schema({
1924
1980
  userId: {
@@ -2296,6 +2352,12 @@ const rolePermissionsSchema = new mongoose.Schema({
2296
2352
  [librechatDataProvider.Permissions.SHARE]: { type: Boolean },
2297
2353
  [librechatDataProvider.Permissions.SHARE_PUBLIC]: { type: Boolean },
2298
2354
  },
2355
+ [librechatDataProvider.PermissionTypes.REMOTE_AGENTS]: {
2356
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
2357
+ [librechatDataProvider.Permissions.CREATE]: { type: Boolean },
2358
+ [librechatDataProvider.Permissions.SHARE]: { type: Boolean },
2359
+ [librechatDataProvider.Permissions.SHARE_PUBLIC]: { type: Boolean },
2360
+ },
2299
2361
  }, { _id: false });
2300
2362
  const roleSchema = new mongoose.Schema({
2301
2363
  name: { type: String, required: true, unique: true, index: true },
@@ -2453,6 +2515,7 @@ const transactionSchema = new mongoose.Schema({
2453
2515
  inputTokens: { type: Number },
2454
2516
  writeTokens: { type: Number },
2455
2517
  readTokens: { type: Number },
2518
+ messageId: { type: String },
2456
2519
  }, {
2457
2520
  timestamps: true,
2458
2521
  });
@@ -2680,6 +2743,7 @@ groupSchema.index({ idOnTheSource: 1, source: 1 }, {
2680
2743
  });
2681
2744
  groupSchema.index({ memberIds: 1 });
2682
2745
 
2746
+ const CANCEL_RATE = 1.15;
2683
2747
  /**
2684
2748
  * Checks if the connected MongoDB deployment supports transactions
2685
2749
  * This requires a MongoDB replica set configuration
@@ -2870,8 +2934,8 @@ const createMeiliMongooseModel = ({ index, attributesToIndex, syncOptions, }) =>
2870
2934
  }
2871
2935
  /**
2872
2936
  * Synchronizes data between the MongoDB collection and the MeiliSearch index by
2873
- * incrementally indexing only documents where `expiredAt` is `null` and `_meiliIndex` is `false`
2874
- * (i.e., non-expired documents that have not yet been indexed).
2937
+ * incrementally indexing only documents where `expiredAt` is `null` and `_meiliIndex` is not `true`
2938
+ * (i.e., non-expired documents that have not yet been indexed, including those with missing or null `_meiliIndex`).
2875
2939
  * */
2876
2940
  static async syncWithMeili() {
2877
2941
  const startTime = Date.now();
@@ -2896,7 +2960,7 @@ const createMeiliMongooseModel = ({ index, attributesToIndex, syncOptions, }) =>
2896
2960
  while (hasMore) {
2897
2961
  const query = {
2898
2962
  expiredAt: null,
2899
- _meiliIndex: false,
2963
+ _meiliIndex: { $ne: true },
2900
2964
  };
2901
2965
  try {
2902
2966
  const documents = await this.find(query)
@@ -3359,6 +3423,10 @@ function createAgentModel(mongoose) {
3359
3423
  return mongoose.models.Agent || mongoose.model('Agent', agentSchema);
3360
3424
  }
3361
3425
 
3426
+ function createAgentApiKeyModel(mongoose) {
3427
+ return (mongoose.models.AgentApiKey || mongoose.model('AgentApiKey', agentApiKeySchema));
3428
+ }
3429
+
3362
3430
  /**
3363
3431
  * Creates or returns the AgentCategory model using the provided mongoose instance and schema
3364
3432
  */
@@ -3522,7 +3590,7 @@ const accessRoleSchema = new mongoose.Schema({
3522
3590
  description: String,
3523
3591
  resourceType: {
3524
3592
  type: String,
3525
- enum: ['agent', 'project', 'file', 'promptGroup', 'mcpServer'],
3593
+ enum: ['agent', 'project', 'file', 'promptGroup', 'mcpServer', 'remoteAgent'],
3526
3594
  required: true,
3527
3595
  default: 'agent',
3528
3596
  },
@@ -3622,6 +3690,7 @@ function createModels(mongoose) {
3622
3690
  Conversation: createConversationModel(mongoose),
3623
3691
  Message: createMessageModel(mongoose),
3624
3692
  Agent: createAgentModel(mongoose),
3693
+ AgentApiKey: createAgentApiKeyModel(mongoose),
3625
3694
  AgentCategory: createAgentCategoryModel(mongoose),
3626
3695
  MCPServer: createMCPServerModel(mongoose),
3627
3696
  Role: createRoleModel(mongoose),
@@ -4435,30 +4504,33 @@ function createFileMethods(mongoose) {
4435
4504
  return await query.sort(sortOptions).lean();
4436
4505
  }
4437
4506
  /**
4438
- * Retrieves tool files (files that are embedded or have a fileIdentifier) from an array of file IDs
4507
+ * Retrieves tool files (files that are embedded or have a fileIdentifier) from an array of file IDs.
4508
+ * Note: execute_code files are handled separately by getCodeGeneratedFiles.
4439
4509
  * @param fileIds - Array of file_id strings to search for
4440
4510
  * @param toolResourceSet - Optional filter for tool resources
4441
4511
  * @returns Files that match the criteria
4442
4512
  */
4443
4513
  async function getToolFilesByIds(fileIds, toolResourceSet) {
4444
- var _a, _b, _c;
4445
4514
  if (!fileIds || !fileIds.length || !(toolResourceSet === null || toolResourceSet === void 0 ? void 0 : toolResourceSet.size)) {
4446
4515
  return [];
4447
4516
  }
4448
4517
  try {
4449
- const filter = {
4450
- file_id: { $in: fileIds },
4451
- $or: [],
4452
- };
4518
+ const orConditions = [];
4453
4519
  if (toolResourceSet.has(librechatDataProvider.EToolResources.context)) {
4454
- (_a = filter.$or) === null || _a === void 0 ? void 0 : _a.push({ text: { $exists: true, $ne: null }, context: librechatDataProvider.FileContext.agents });
4520
+ orConditions.push({ text: { $exists: true, $ne: null }, context: librechatDataProvider.FileContext.agents });
4455
4521
  }
4456
4522
  if (toolResourceSet.has(librechatDataProvider.EToolResources.file_search)) {
4457
- (_b = filter.$or) === null || _b === void 0 ? void 0 : _b.push({ embedded: true });
4523
+ orConditions.push({ embedded: true });
4458
4524
  }
4459
- if (toolResourceSet.has(librechatDataProvider.EToolResources.execute_code)) {
4460
- (_c = filter.$or) === null || _c === void 0 ? void 0 : _c.push({ 'metadata.fileIdentifier': { $exists: true } });
4525
+ // If no conditions to match, return empty
4526
+ if (orConditions.length === 0) {
4527
+ return [];
4461
4528
  }
4529
+ const filter = {
4530
+ file_id: { $in: fileIds },
4531
+ context: { $ne: librechatDataProvider.FileContext.execute_code },
4532
+ $or: orConditions,
4533
+ };
4462
4534
  const selectFields = { text: 0 };
4463
4535
  const sortOptions = { updatedAt: -1 };
4464
4536
  const results = await getFiles(filter, sortOptions, selectFields);
@@ -4469,6 +4541,91 @@ function createFileMethods(mongoose) {
4469
4541
  throw new Error('Error retrieving tool files');
4470
4542
  }
4471
4543
  }
4544
+ /**
4545
+ * Retrieves files generated by code execution for a given conversation.
4546
+ * These files are stored locally with fileIdentifier metadata for code env re-upload.
4547
+ *
4548
+ * @param conversationId - The conversation ID to search for
4549
+ * @param messageIds - Array of messageIds to filter by (for linear thread filtering).
4550
+ * While technically optional, this function returns empty if not provided.
4551
+ * This is intentional: code-generated files must be filtered by thread to avoid
4552
+ * including files from other branches of a conversation.
4553
+ * @returns Files generated by code execution in the conversation, filtered by messageIds
4554
+ */
4555
+ async function getCodeGeneratedFiles(conversationId, messageIds) {
4556
+ if (!conversationId) {
4557
+ return [];
4558
+ }
4559
+ /**
4560
+ * Return early if messageIds not provided - this is intentional behavior.
4561
+ * Code-generated files must be filtered by thread messageIds to ensure we only
4562
+ * return files relevant to the current conversation branch, not orphaned files
4563
+ * from other branches or deleted messages.
4564
+ */
4565
+ if (!messageIds || messageIds.length === 0) {
4566
+ return [];
4567
+ }
4568
+ try {
4569
+ const filter = {
4570
+ conversationId,
4571
+ context: librechatDataProvider.FileContext.execute_code,
4572
+ messageId: { $exists: true, $in: messageIds },
4573
+ 'metadata.fileIdentifier': { $exists: true },
4574
+ };
4575
+ const selectFields = { text: 0 };
4576
+ const sortOptions = { createdAt: 1 };
4577
+ const results = await getFiles(filter, sortOptions, selectFields);
4578
+ return results !== null && results !== void 0 ? results : [];
4579
+ }
4580
+ catch (error) {
4581
+ logger$1.error('[getCodeGeneratedFiles] Error retrieving code generated files:', error);
4582
+ return [];
4583
+ }
4584
+ }
4585
+ /**
4586
+ * Retrieves user-uploaded execute_code files (not code-generated) by their file IDs.
4587
+ * These are files with fileIdentifier metadata but context is NOT execute_code (e.g., agents or message_attachment).
4588
+ * File IDs should be collected from message.files arrays in the current thread.
4589
+ * @param fileIds - Array of file IDs to fetch (from message.files in the thread)
4590
+ * @returns User-uploaded execute_code files
4591
+ */
4592
+ async function getUserCodeFiles(fileIds) {
4593
+ if (!fileIds || fileIds.length === 0) {
4594
+ return [];
4595
+ }
4596
+ try {
4597
+ const filter = {
4598
+ file_id: { $in: fileIds },
4599
+ context: { $ne: librechatDataProvider.FileContext.execute_code },
4600
+ 'metadata.fileIdentifier': { $exists: true },
4601
+ };
4602
+ const selectFields = { text: 0 };
4603
+ const sortOptions = { createdAt: 1 };
4604
+ const results = await getFiles(filter, sortOptions, selectFields);
4605
+ return results !== null && results !== void 0 ? results : [];
4606
+ }
4607
+ catch (error) {
4608
+ logger$1.error('[getUserCodeFiles] Error retrieving user code files:', error);
4609
+ return [];
4610
+ }
4611
+ }
4612
+ /**
4613
+ * Atomically claims a file_id for a code-execution output by compound key.
4614
+ * Uses $setOnInsert so concurrent calls for the same (filename, conversationId)
4615
+ * converge on a single record instead of creating duplicates.
4616
+ */
4617
+ async function claimCodeFile(data) {
4618
+ const File = mongoose.models.File;
4619
+ const result = await File.findOneAndUpdate({
4620
+ filename: data.filename,
4621
+ conversationId: data.conversationId,
4622
+ context: librechatDataProvider.FileContext.execute_code,
4623
+ }, { $setOnInsert: { file_id: data.file_id, user: data.user } }, { upsert: true, new: true }).lean();
4624
+ if (!result) {
4625
+ throw new Error(`[claimCodeFile] Failed to claim file "${data.filename}" for conversation ${data.conversationId}`);
4626
+ }
4627
+ return result;
4628
+ }
4472
4629
  /**
4473
4630
  * Creates a new file with a TTL of 1 hour.
4474
4631
  * @param data - The file data to be created, must contain file_id
@@ -4608,6 +4765,9 @@ function createFileMethods(mongoose) {
4608
4765
  findFileById,
4609
4766
  getFiles,
4610
4767
  getToolFilesByIds,
4768
+ getCodeGeneratedFiles,
4769
+ getUserCodeFiles,
4770
+ claimCodeFile,
4611
4771
  createFile,
4612
4772
  updateFile,
4613
4773
  updateFileUsage,
@@ -4966,6 +5126,139 @@ function createAgentCategoryMethods(mongoose) {
4966
5126
  };
4967
5127
  }
4968
5128
 
5129
+ const API_KEY_PREFIX = 'sk-';
5130
+ const API_KEY_LENGTH = 32;
5131
+ function createAgentApiKeyMethods(mongoose) {
5132
+ async function generateApiKey() {
5133
+ const randomPart = await getRandomValues(API_KEY_LENGTH);
5134
+ const key = `${API_KEY_PREFIX}${randomPart}`;
5135
+ const keyHash = await hashToken(key);
5136
+ const keyPrefix = key.slice(0, 8);
5137
+ return { key, keyHash, keyPrefix };
5138
+ }
5139
+ async function createAgentApiKey(data) {
5140
+ try {
5141
+ const AgentApiKey = mongoose.models.AgentApiKey;
5142
+ const { key, keyHash, keyPrefix } = await generateApiKey();
5143
+ const apiKeyDoc = await AgentApiKey.create({
5144
+ userId: data.userId,
5145
+ name: data.name,
5146
+ keyHash,
5147
+ keyPrefix,
5148
+ expiresAt: data.expiresAt || undefined,
5149
+ });
5150
+ return {
5151
+ id: apiKeyDoc._id.toString(),
5152
+ name: apiKeyDoc.name,
5153
+ keyPrefix,
5154
+ key,
5155
+ createdAt: apiKeyDoc.createdAt,
5156
+ expiresAt: apiKeyDoc.expiresAt,
5157
+ };
5158
+ }
5159
+ catch (error) {
5160
+ logger$1.error('[createAgentApiKey] Error creating API key:', error);
5161
+ throw error;
5162
+ }
5163
+ }
5164
+ async function validateAgentApiKey(apiKey) {
5165
+ try {
5166
+ const AgentApiKey = mongoose.models.AgentApiKey;
5167
+ const keyHash = await hashToken(apiKey);
5168
+ const keyDoc = (await AgentApiKey.findOne({ keyHash }).lean());
5169
+ if (!keyDoc) {
5170
+ return null;
5171
+ }
5172
+ if (keyDoc.expiresAt && new Date(keyDoc.expiresAt) < new Date()) {
5173
+ return null;
5174
+ }
5175
+ await AgentApiKey.updateOne({ _id: keyDoc._id }, { $set: { lastUsedAt: new Date() } });
5176
+ return {
5177
+ userId: keyDoc.userId,
5178
+ keyId: keyDoc._id,
5179
+ };
5180
+ }
5181
+ catch (error) {
5182
+ logger$1.error('[validateAgentApiKey] Error validating API key:', error);
5183
+ return null;
5184
+ }
5185
+ }
5186
+ async function listAgentApiKeys(userId) {
5187
+ try {
5188
+ const AgentApiKey = mongoose.models.AgentApiKey;
5189
+ const keys = (await AgentApiKey.find({ userId })
5190
+ .sort({ createdAt: -1 })
5191
+ .lean());
5192
+ return keys.map((key) => ({
5193
+ id: key._id.toString(),
5194
+ name: key.name,
5195
+ keyPrefix: key.keyPrefix,
5196
+ lastUsedAt: key.lastUsedAt,
5197
+ expiresAt: key.expiresAt,
5198
+ createdAt: key.createdAt,
5199
+ }));
5200
+ }
5201
+ catch (error) {
5202
+ logger$1.error('[listAgentApiKeys] Error listing API keys:', error);
5203
+ throw error;
5204
+ }
5205
+ }
5206
+ async function deleteAgentApiKey(keyId, userId) {
5207
+ try {
5208
+ const AgentApiKey = mongoose.models.AgentApiKey;
5209
+ const result = await AgentApiKey.deleteOne({ _id: keyId, userId });
5210
+ return result.deletedCount > 0;
5211
+ }
5212
+ catch (error) {
5213
+ logger$1.error('[deleteAgentApiKey] Error deleting API key:', error);
5214
+ throw error;
5215
+ }
5216
+ }
5217
+ async function deleteAllAgentApiKeys(userId) {
5218
+ try {
5219
+ const AgentApiKey = mongoose.models.AgentApiKey;
5220
+ const result = await AgentApiKey.deleteMany({ userId });
5221
+ return result.deletedCount;
5222
+ }
5223
+ catch (error) {
5224
+ logger$1.error('[deleteAllAgentApiKeys] Error deleting all API keys:', error);
5225
+ throw error;
5226
+ }
5227
+ }
5228
+ async function getAgentApiKeyById(keyId, userId) {
5229
+ try {
5230
+ const AgentApiKey = mongoose.models.AgentApiKey;
5231
+ const keyDoc = (await AgentApiKey.findOne({
5232
+ _id: keyId,
5233
+ userId,
5234
+ }).lean());
5235
+ if (!keyDoc) {
5236
+ return null;
5237
+ }
5238
+ return {
5239
+ id: keyDoc._id.toString(),
5240
+ name: keyDoc.name,
5241
+ keyPrefix: keyDoc.keyPrefix,
5242
+ lastUsedAt: keyDoc.lastUsedAt,
5243
+ expiresAt: keyDoc.expiresAt,
5244
+ createdAt: keyDoc.createdAt,
5245
+ };
5246
+ }
5247
+ catch (error) {
5248
+ logger$1.error('[getAgentApiKeyById] Error getting API key:', error);
5249
+ throw error;
5250
+ }
5251
+ }
5252
+ return {
5253
+ createAgentApiKey,
5254
+ validateAgentApiKey,
5255
+ listAgentApiKeys,
5256
+ deleteAgentApiKey,
5257
+ deleteAllAgentApiKeys,
5258
+ getAgentApiKeyById,
5259
+ };
5260
+ }
5261
+
4969
5262
  const NORMALIZED_LIMIT_DEFAULT = 20;
4970
5263
  const MAX_CREATE_RETRIES = 5;
4971
5264
  const RETRY_BASE_DELAY_MS = 25;
@@ -5466,6 +5759,27 @@ function createAccessRoleMethods(mongoose) {
5466
5759
  resourceType: librechatDataProvider.ResourceType.MCPSERVER,
5467
5760
  permBits: exports.RoleBits.OWNER,
5468
5761
  },
5762
+ {
5763
+ accessRoleId: librechatDataProvider.AccessRoleIds.REMOTE_AGENT_VIEWER,
5764
+ name: 'com_ui_remote_agent_role_viewer',
5765
+ description: 'com_ui_remote_agent_role_viewer_desc',
5766
+ resourceType: librechatDataProvider.ResourceType.REMOTE_AGENT,
5767
+ permBits: exports.RoleBits.VIEWER,
5768
+ },
5769
+ {
5770
+ accessRoleId: librechatDataProvider.AccessRoleIds.REMOTE_AGENT_EDITOR,
5771
+ name: 'com_ui_remote_agent_role_editor',
5772
+ description: 'com_ui_remote_agent_role_editor_desc',
5773
+ resourceType: librechatDataProvider.ResourceType.REMOTE_AGENT,
5774
+ permBits: exports.RoleBits.EDITOR,
5775
+ },
5776
+ {
5777
+ accessRoleId: librechatDataProvider.AccessRoleIds.REMOTE_AGENT_OWNER,
5778
+ name: 'com_ui_remote_agent_role_owner',
5779
+ description: 'com_ui_remote_agent_role_owner_desc',
5780
+ resourceType: librechatDataProvider.ResourceType.REMOTE_AGENT,
5781
+ permBits: exports.RoleBits.OWNER,
5782
+ },
5469
5783
  ];
5470
5784
  const result = {};
5471
5785
  for (const role of defaultRoles) {
@@ -6703,6 +7017,77 @@ function createShareMethods(mongoose) {
6703
7017
  };
6704
7018
  }
6705
7019
 
7020
+ function createTransactionMethods(mongoose) {
7021
+ async function updateBalance({ user, incrementValue, setValues }) {
7022
+ var _a;
7023
+ const maxRetries = 10;
7024
+ let delay = 50;
7025
+ let lastError = null;
7026
+ const Balance = mongoose.models.Balance;
7027
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
7028
+ try {
7029
+ const currentBalanceDoc = await Balance.findOne({ user }).lean();
7030
+ const currentCredits = (_a = currentBalanceDoc === null || currentBalanceDoc === void 0 ? void 0 : currentBalanceDoc.tokenCredits) !== null && _a !== void 0 ? _a : 0;
7031
+ const newCredits = Math.max(0, currentCredits + incrementValue);
7032
+ const updatePayload = {
7033
+ $set: {
7034
+ tokenCredits: newCredits,
7035
+ ...(setValues !== null && setValues !== void 0 ? setValues : {}),
7036
+ },
7037
+ };
7038
+ if (currentBalanceDoc) {
7039
+ const updatedBalance = await Balance.findOneAndUpdate({ user, tokenCredits: currentCredits }, updatePayload, { new: true }).lean();
7040
+ if (updatedBalance) {
7041
+ return updatedBalance;
7042
+ }
7043
+ lastError = new Error(`Concurrency conflict for user ${user} on attempt ${attempt}.`);
7044
+ }
7045
+ else {
7046
+ try {
7047
+ const updatedBalance = await Balance.findOneAndUpdate({ user }, updatePayload, {
7048
+ upsert: true,
7049
+ new: true,
7050
+ }).lean();
7051
+ if (updatedBalance) {
7052
+ return updatedBalance;
7053
+ }
7054
+ lastError = new Error(`Upsert race condition suspected for user ${user} on attempt ${attempt}.`);
7055
+ }
7056
+ catch (error) {
7057
+ if (error instanceof Error &&
7058
+ 'code' in error &&
7059
+ error.code === 11000) {
7060
+ lastError = error;
7061
+ }
7062
+ else {
7063
+ throw error;
7064
+ }
7065
+ }
7066
+ }
7067
+ }
7068
+ catch (error) {
7069
+ logger$1.error(`[updateBalance] Error during attempt ${attempt} for user ${user}:`, error);
7070
+ lastError = error instanceof Error ? error : new Error(String(error));
7071
+ }
7072
+ if (attempt < maxRetries) {
7073
+ const jitter = Math.random() * delay * 0.5;
7074
+ await new Promise((resolve) => setTimeout(resolve, delay + jitter));
7075
+ delay = Math.min(delay * 2, 2000);
7076
+ }
7077
+ }
7078
+ logger$1.error(`[updateBalance] Failed to update balance for user ${user} after ${maxRetries} attempts.`);
7079
+ throw (lastError !== null && lastError !== void 0 ? lastError : new Error(`Failed to update balance for user ${user} after maximum retries due to persistent conflicts.`));
7080
+ }
7081
+ /** Bypasses document middleware; all computed fields must be pre-calculated before calling. */
7082
+ async function bulkInsertTransactions(docs) {
7083
+ const Transaction = mongoose.models.Transaction;
7084
+ if (docs.length) {
7085
+ await Transaction.insertMany(docs);
7086
+ }
7087
+ }
7088
+ return { updateBalance, bulkInsertTransactions };
7089
+ }
7090
+
6706
7091
  /**
6707
7092
  * Creates all database methods for all collections
6708
7093
  * @param mongoose - Mongoose instance
@@ -6717,19 +7102,23 @@ function createMethods(mongoose) {
6717
7102
  ...createFileMethods(mongoose),
6718
7103
  ...createMemoryMethods(mongoose),
6719
7104
  ...createAgentCategoryMethods(mongoose),
7105
+ ...createAgentApiKeyMethods(mongoose),
6720
7106
  ...createMCPServerMethods(mongoose),
6721
7107
  ...createAccessRoleMethods(mongoose),
6722
7108
  ...createUserGroupMethods(mongoose),
6723
7109
  ...createAclEntryMethods(mongoose),
6724
7110
  ...createShareMethods(mongoose),
6725
7111
  ...createPluginAuthMethods(mongoose),
7112
+ ...createTransactionMethods(mongoose),
6726
7113
  };
6727
7114
  }
6728
7115
 
6729
7116
  exports.AppService = AppService;
7117
+ exports.CANCEL_RATE = CANCEL_RATE;
6730
7118
  exports.DEFAULT_REFRESH_TOKEN_EXPIRY = DEFAULT_REFRESH_TOKEN_EXPIRY;
6731
7119
  exports.DEFAULT_SESSION_EXPIRY = DEFAULT_SESSION_EXPIRY;
6732
7120
  exports.actionSchema = Action;
7121
+ exports.agentApiKeySchema = agentApiKeySchema;
6733
7122
  exports.agentCategorySchema = agentCategorySchema;
6734
7123
  exports.agentSchema = agentSchema;
6735
7124
  exports.agentsConfigSetup = agentsConfigSetup;