@librechat/data-schemas 0.0.23 → 0.0.30

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.es.js CHANGED
@@ -1,1873 +1,2376 @@
1
- import { PermissionBits, FileSources, Constants, PermissionTypes, Permissions, SystemRoles, ResourceType, PrincipalType, PrincipalModel, roleDefaults, AccessRoleIds } from 'librechat-data-provider';
2
- import jwt from 'jsonwebtoken';
3
- import { webcrypto } from 'node:crypto';
4
- import mongoose, { Schema, Types } from 'mongoose';
1
+ import { EModelEndpoint, agentsEndpointSchema, memorySchema, removeNullishValues, SafeSearchTypes, normalizeEndpointName, defaultAssistantsVersion, Capabilities, assistantEndpointSchema, validateAzureGroups, mapModelToAzureConfig, OCRStrategy, getConfigDefaults, PermissionBits, FileSources, Constants, PermissionTypes, Permissions, SystemRoles, ResourceType, PrincipalType, PrincipalModel, roleDefaults, AccessRoleIds } from 'librechat-data-provider';
5
2
  import winston from 'winston';
6
3
  import 'winston-daily-rotate-file';
7
4
  import { klona } from 'klona';
8
5
  import path from 'path';
6
+ import jwt from 'jsonwebtoken';
7
+ import { webcrypto } from 'node:crypto';
8
+ import mongoose, { Schema, Types } from 'mongoose';
9
9
  import _ from 'lodash';
10
10
  import { MeiliSearch } from 'meilisearch';
11
11
  import { nanoid } from 'nanoid';
12
12
 
13
13
  /**
14
- * Common role combinations
14
+ * Sets up the Agents configuration from the config (`librechat.yaml`) file.
15
+ * If no agents config is defined, uses the provided defaults or parses empty object.
16
+ *
17
+ * @param config - The loaded custom configuration.
18
+ * @param [defaultConfig] - Default configuration from getConfigDefaults.
19
+ * @returns The Agents endpoint configuration.
15
20
  */
16
- var RoleBits;
17
- (function (RoleBits) {
18
- /** 0001 = 1 */
19
- RoleBits[RoleBits["VIEWER"] = PermissionBits.VIEW] = "VIEWER";
20
- /** 0011 = 3 */
21
- RoleBits[RoleBits["EDITOR"] = PermissionBits.VIEW | PermissionBits.EDIT] = "EDITOR";
22
- /** 0111 = 7 */
23
- RoleBits[RoleBits["MANAGER"] = PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE] = "MANAGER";
24
- /** 1111 = 15 */
25
- RoleBits[RoleBits["OWNER"] = PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE] = "OWNER";
26
- })(RoleBits || (RoleBits = {}));
21
+ function agentsConfigSetup(config, defaultConfig) {
22
+ var _a;
23
+ const agentsConfig = (_a = config === null || config === void 0 ? void 0 : config.endpoints) === null || _a === void 0 ? void 0 : _a[EModelEndpoint.agents];
24
+ if (!agentsConfig) {
25
+ return defaultConfig || agentsEndpointSchema.parse({});
26
+ }
27
+ const parsedConfig = agentsEndpointSchema.parse(agentsConfig);
28
+ return parsedConfig;
29
+ }
27
30
 
28
- async function signPayload({ payload, secret, expirationTime, }) {
29
- return jwt.sign(payload, secret, { expiresIn: expirationTime });
31
+ const hasValidAgent = (agent) => !!agent &&
32
+ (('id' in agent && !!agent.id) ||
33
+ ('provider' in agent && 'model' in agent && !!agent.provider && !!agent.model));
34
+ const isDisabled = (config) => !config || config.disabled === true;
35
+ function loadMemoryConfig(config) {
36
+ var _a;
37
+ if (!config)
38
+ return undefined;
39
+ if (isDisabled(config))
40
+ return config;
41
+ if (!hasValidAgent(config.agent)) {
42
+ return { ...config, disabled: true };
43
+ }
44
+ const charLimit = (_a = memorySchema.shape.charLimit.safeParse(config.charLimit).data) !== null && _a !== void 0 ? _a : 10000;
45
+ return { ...config, charLimit };
30
46
  }
31
- async function hashToken(str) {
32
- const data = new TextEncoder().encode(str);
33
- const hashBuffer = await webcrypto.subtle.digest('SHA-256', data);
34
- return Buffer.from(hashBuffer).toString('hex');
47
+ function isMemoryEnabled(config) {
48
+ if (isDisabled(config))
49
+ return false;
50
+ return hasValidAgent(config.agent);
35
51
  }
36
52
 
37
- // Define the Auth sub-schema with type-safety.
38
- const AuthSchema = new Schema({
39
- authorization_type: { type: String },
40
- custom_auth_header: { type: String },
41
- type: { type: String, enum: ['service_http', 'oauth', 'none'] },
42
- authorization_content_type: { type: String },
43
- authorization_url: { type: String },
44
- client_url: { type: String },
45
- scope: { type: String },
46
- token_exchange_method: { type: String, enum: ['default_post', 'basic_auth_header', null] },
47
- }, { _id: false });
48
- const Action = new Schema({
49
- user: {
50
- type: mongoose.Schema.Types.ObjectId,
51
- ref: 'User',
52
- index: true,
53
- required: true,
54
- },
55
- action_id: {
56
- type: String,
57
- index: true,
58
- required: true,
59
- },
60
- type: {
61
- type: String,
62
- default: 'action_prototype',
63
- },
64
- settings: Schema.Types.Mixed,
65
- agent_id: String,
66
- assistant_id: String,
67
- metadata: {
68
- api_key: String,
69
- auth: AuthSchema,
70
- domain: {
71
- type: String,
72
- required: true,
53
+ /**
54
+ * Loads the default interface object.
55
+ * @param params - The loaded custom configuration.
56
+ * @param params.config - The loaded custom configuration.
57
+ * @param params.configDefaults - The custom configuration default values.
58
+ * @returns default interface object.
59
+ */
60
+ async function loadDefaultInterface({ config, configDefaults, }) {
61
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
62
+ const { interface: interfaceConfig } = config !== null && config !== void 0 ? config : {};
63
+ const { interface: defaults } = configDefaults;
64
+ const hasModelSpecs = ((_c = (_b = (_a = config === null || config === void 0 ? void 0 : config.modelSpecs) === null || _a === void 0 ? void 0 : _a.list) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) > 0;
65
+ const includesAddedEndpoints = ((_f = (_e = (_d = config === null || config === void 0 ? void 0 : config.modelSpecs) === null || _d === void 0 ? void 0 : _d.addedEndpoints) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0) > 0;
66
+ const memoryConfig = config === null || config === void 0 ? void 0 : config.memory;
67
+ const memoryEnabled = isMemoryEnabled(memoryConfig);
68
+ /** Only disable memories if memory config is present but disabled/invalid */
69
+ const shouldDisableMemories = memoryConfig && !memoryEnabled;
70
+ const loadedInterface = removeNullishValues({
71
+ // UI elements - use schema defaults
72
+ endpointsMenu: (_g = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.endpointsMenu) !== null && _g !== void 0 ? _g : (hasModelSpecs ? false : defaults.endpointsMenu),
73
+ modelSelect: (_h = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.modelSelect) !== null && _h !== void 0 ? _h : (hasModelSpecs ? includesAddedEndpoints : defaults.modelSelect),
74
+ parameters: (_j = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.parameters) !== null && _j !== void 0 ? _j : (hasModelSpecs ? false : defaults.parameters),
75
+ presets: (_k = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.presets) !== null && _k !== void 0 ? _k : (hasModelSpecs ? false : defaults.presets),
76
+ sidePanel: (_l = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.sidePanel) !== null && _l !== void 0 ? _l : defaults.sidePanel,
77
+ privacyPolicy: (_m = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.privacyPolicy) !== null && _m !== void 0 ? _m : defaults.privacyPolicy,
78
+ termsOfService: (_o = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.termsOfService) !== null && _o !== void 0 ? _o : defaults.termsOfService,
79
+ mcpServers: (_p = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.mcpServers) !== null && _p !== void 0 ? _p : defaults.mcpServers,
80
+ customWelcome: (_q = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.customWelcome) !== null && _q !== void 0 ? _q : defaults.customWelcome,
81
+ // Permissions - only include if explicitly configured
82
+ bookmarks: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.bookmarks,
83
+ memories: shouldDisableMemories ? false : interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.memories,
84
+ prompts: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.prompts,
85
+ multiConvo: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.multiConvo,
86
+ agents: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.agents,
87
+ temporaryChat: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.temporaryChat,
88
+ runCode: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.runCode,
89
+ webSearch: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.webSearch,
90
+ fileSearch: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.fileSearch,
91
+ fileCitations: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.fileCitations,
92
+ peoplePicker: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.peoplePicker,
93
+ marketplace: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.marketplace,
94
+ });
95
+ return loadedInterface;
96
+ }
97
+
98
+ /**
99
+ * ESM-native object traversal utility
100
+ * Simplified implementation focused on the forEach use case
101
+ */
102
+ function isObject(value) {
103
+ if (value === null || typeof value !== 'object') {
104
+ return false;
105
+ }
106
+ // Treat these built-in types as leaf nodes, not objects to traverse
107
+ if (value instanceof Date)
108
+ return false;
109
+ if (value instanceof RegExp)
110
+ return false;
111
+ if (value instanceof Error)
112
+ return false;
113
+ if (value instanceof URL)
114
+ return false;
115
+ // Check for Buffer (Node.js)
116
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value))
117
+ return false;
118
+ // Check for TypedArrays and ArrayBuffer
119
+ if (ArrayBuffer.isView(value))
120
+ return false;
121
+ if (value instanceof ArrayBuffer)
122
+ return false;
123
+ if (value instanceof SharedArrayBuffer)
124
+ return false;
125
+ // Check for other built-in types that shouldn't be traversed
126
+ if (value instanceof Promise)
127
+ return false;
128
+ if (value instanceof WeakMap)
129
+ return false;
130
+ if (value instanceof WeakSet)
131
+ return false;
132
+ if (value instanceof Map)
133
+ return false;
134
+ if (value instanceof Set)
135
+ return false;
136
+ // Check if it's a primitive wrapper object
137
+ const stringTag = Object.prototype.toString.call(value);
138
+ if (stringTag === '[object Boolean]' ||
139
+ stringTag === '[object Number]' ||
140
+ stringTag === '[object String]') {
141
+ return false;
142
+ }
143
+ return true;
144
+ }
145
+ // Helper to safely set a property on an object or array
146
+ function setProperty(obj, key, value) {
147
+ if (Array.isArray(obj) && typeof key === 'number') {
148
+ obj[key] = value;
149
+ }
150
+ else if (!Array.isArray(obj) && typeof key === 'string') {
151
+ obj[key] = value;
152
+ }
153
+ else if (!Array.isArray(obj) && typeof key === 'number') {
154
+ // Handle numeric keys on objects
155
+ obj[key] = value;
156
+ }
157
+ }
158
+ // Helper to safely delete a property from an object
159
+ function deleteProperty(obj, key) {
160
+ if (Array.isArray(obj) && typeof key === 'number') {
161
+ // For arrays, we should use splice, but this is handled in remove()
162
+ // This function is only called for non-array deletion
163
+ return;
164
+ }
165
+ if (!Array.isArray(obj)) {
166
+ delete obj[key];
167
+ }
168
+ }
169
+ function forEach(obj, callback) {
170
+ const visited = new WeakSet();
171
+ function walk(node, path = [], parent) {
172
+ // Check for circular references
173
+ let circular = null;
174
+ if (isObject(node)) {
175
+ if (visited.has(node)) {
176
+ // Find the circular reference in the parent chain
177
+ let p = parent;
178
+ while (p) {
179
+ if (p.node === node) {
180
+ circular = p;
181
+ break;
182
+ }
183
+ p = p.parent;
184
+ }
185
+ return; // Skip circular references
186
+ }
187
+ visited.add(node);
188
+ }
189
+ const key = path.length > 0 ? path[path.length - 1] : undefined;
190
+ const isRoot = path.length === 0;
191
+ const level = path.length;
192
+ // Determine if this is a leaf node
193
+ const isLeaf = !isObject(node) ||
194
+ (Array.isArray(node) && node.length === 0) ||
195
+ Object.keys(node).length === 0;
196
+ // Create context
197
+ const context = {
198
+ node,
199
+ path: [...path],
200
+ parent,
201
+ key,
202
+ isLeaf,
203
+ notLeaf: !isLeaf,
204
+ isRoot,
205
+ notRoot: !isRoot,
206
+ level,
207
+ circular,
208
+ update(value) {
209
+ if (!isRoot && parent && key !== undefined && isObject(parent.node)) {
210
+ setProperty(parent.node, key, value);
211
+ }
212
+ this.node = value;
213
+ },
214
+ remove() {
215
+ if (!isRoot && parent && key !== undefined && isObject(parent.node)) {
216
+ if (Array.isArray(parent.node) && typeof key === 'number') {
217
+ parent.node.splice(key, 1);
218
+ }
219
+ else {
220
+ deleteProperty(parent.node, key);
221
+ }
222
+ }
223
+ },
224
+ };
225
+ // Call the callback with the context
226
+ callback.call(context, node);
227
+ // Traverse children if not circular and is an object
228
+ if (!circular && isObject(node) && !isLeaf) {
229
+ if (Array.isArray(node)) {
230
+ for (let i = 0; i < node.length; i++) {
231
+ walk(node[i], [...path, i], context);
232
+ }
233
+ }
234
+ else {
235
+ for (const [childKey, childValue] of Object.entries(node)) {
236
+ walk(childValue, [...path, childKey], context);
237
+ }
238
+ }
239
+ }
240
+ }
241
+ walk(obj);
242
+ }
243
+ // Main traverse function that returns an object with forEach method
244
+ function traverse(obj) {
245
+ return {
246
+ forEach(callback) {
247
+ forEach(obj, callback);
73
248
  },
74
- privacy_policy_url: String,
75
- raw_spec: String,
76
- oauth_client_id: String,
77
- oauth_client_secret: String,
78
- },
79
- });
249
+ };
250
+ }
80
251
 
81
- const agentSchema = new Schema({
82
- id: {
83
- type: String,
84
- index: true,
85
- unique: true,
86
- required: true,
87
- },
88
- name: {
89
- type: String,
90
- },
91
- description: {
92
- type: String,
93
- },
94
- instructions: {
95
- type: String,
96
- },
97
- avatar: {
98
- type: Schema.Types.Mixed,
99
- default: undefined,
100
- },
101
- provider: {
102
- type: String,
103
- required: true,
104
- },
105
- model: {
106
- type: String,
107
- required: true,
108
- },
109
- model_parameters: {
110
- type: Object,
111
- },
112
- artifacts: {
113
- type: String,
114
- },
115
- access_level: {
116
- type: Number,
117
- },
118
- recursion_limit: {
119
- type: Number,
120
- },
121
- tools: {
122
- type: [String],
123
- default: undefined,
124
- },
125
- tool_kwargs: {
126
- type: [{ type: Schema.Types.Mixed }],
127
- },
128
- actions: {
129
- type: [String],
130
- default: undefined,
131
- },
132
- author: {
133
- type: Schema.Types.ObjectId,
134
- ref: 'User',
135
- required: true,
136
- },
137
- authorName: {
138
- type: String,
139
- default: undefined,
140
- },
141
- hide_sequential_outputs: {
142
- type: Boolean,
143
- },
144
- end_after_tools: {
145
- type: Boolean,
146
- },
147
- agent_ids: {
148
- type: [String],
149
- },
150
- isCollaborative: {
151
- type: Boolean,
152
- default: undefined,
153
- },
154
- conversation_starters: {
155
- type: [String],
156
- default: [],
157
- },
158
- tool_resources: {
159
- type: Schema.Types.Mixed,
160
- default: {},
161
- },
162
- projectIds: {
163
- type: [Schema.Types.ObjectId],
164
- ref: 'Project',
165
- index: true,
166
- },
167
- versions: {
168
- type: [Schema.Types.Mixed],
169
- default: [],
170
- },
171
- category: {
172
- type: String,
173
- trim: true,
174
- index: true,
175
- default: 'general',
176
- },
177
- support_contact: {
178
- type: Schema.Types.Mixed,
179
- default: undefined,
180
- },
181
- is_promoted: {
182
- type: Boolean,
183
- default: false,
184
- index: true,
185
- },
186
- }, {
187
- timestamps: true,
188
- });
189
- agentSchema.index({ updatedAt: -1, _id: 1 });
190
-
191
- const agentCategorySchema = new Schema({
192
- value: {
193
- type: String,
194
- required: true,
195
- unique: true,
196
- trim: true,
197
- lowercase: true,
198
- index: true,
199
- },
200
- label: {
201
- type: String,
202
- required: true,
203
- trim: true,
204
- },
205
- description: {
206
- type: String,
207
- trim: true,
208
- default: '',
209
- },
210
- order: {
211
- type: Number,
212
- default: 0,
213
- index: true,
214
- },
215
- isActive: {
216
- type: Boolean,
217
- default: true,
218
- index: true,
219
- },
220
- custom: {
221
- type: Boolean,
222
- default: false,
223
- },
224
- }, {
225
- timestamps: true,
252
+ const SPLAT_SYMBOL = Symbol.for('splat');
253
+ const MESSAGE_SYMBOL = Symbol.for('message');
254
+ const CONSOLE_JSON_STRING_LENGTH = parseInt(process.env.CONSOLE_JSON_STRING_LENGTH || '', 10) || 255;
255
+ const DEBUG_MESSAGE_LENGTH = parseInt(process.env.DEBUG_MESSAGE_LENGTH || '', 10) || 150;
256
+ const sensitiveKeys = [
257
+ /^(sk-)[^\s]+/, // OpenAI API key pattern
258
+ /(Bearer )[^\s]+/, // Header: Bearer token pattern
259
+ /(api-key:? )[^\s]+/, // Header: API key pattern
260
+ /(key=)[^\s]+/, // URL query param: sensitive key pattern (Google)
261
+ ];
262
+ /**
263
+ * Determines if a given value string is sensitive and returns matching regex patterns.
264
+ *
265
+ * @param valueStr - The value string to check.
266
+ * @returns An array of regex patterns that match the value string.
267
+ */
268
+ function getMatchingSensitivePatterns(valueStr) {
269
+ if (valueStr) {
270
+ // Filter and return all regex patterns that match the value string
271
+ return sensitiveKeys.filter((regex) => regex.test(valueStr));
272
+ }
273
+ return [];
274
+ }
275
+ /**
276
+ * Redacts sensitive information from a console message and trims it to a specified length if provided.
277
+ * @param str - The console message to be redacted.
278
+ * @param trimLength - The optional length at which to trim the redacted message.
279
+ * @returns The redacted and optionally trimmed console message.
280
+ */
281
+ function redactMessage(str, trimLength) {
282
+ if (!str) {
283
+ return '';
284
+ }
285
+ const patterns = getMatchingSensitivePatterns(str);
286
+ patterns.forEach((pattern) => {
287
+ str = str.replace(pattern, '$1[REDACTED]');
288
+ });
289
+ return str;
290
+ }
291
+ /**
292
+ * Redacts sensitive information from log messages if the log level is 'error'.
293
+ * Note: Intentionally mutates the object.
294
+ * @param info - The log information object.
295
+ * @returns The modified log information object.
296
+ */
297
+ const redactFormat = winston.format((info) => {
298
+ if (info.level === 'error') {
299
+ // Type guard to ensure message is a string
300
+ if (typeof info.message === 'string') {
301
+ info.message = redactMessage(info.message);
302
+ }
303
+ // Handle MESSAGE_SYMBOL with type safety
304
+ const symbolValue = info[MESSAGE_SYMBOL];
305
+ if (typeof symbolValue === 'string') {
306
+ info[MESSAGE_SYMBOL] = redactMessage(symbolValue);
307
+ }
308
+ }
309
+ return info;
226
310
  });
227
- agentCategorySchema.index({ isActive: 1, order: 1 });
228
- agentCategorySchema.index({ order: 1, label: 1 });
229
-
230
- const assistantSchema = new Schema({
231
- user: {
232
- type: Schema.Types.ObjectId,
233
- ref: 'User',
234
- required: true,
235
- },
236
- assistant_id: {
237
- type: String,
238
- index: true,
239
- required: true,
240
- },
241
- avatar: {
242
- type: Schema.Types.Mixed,
243
- default: undefined,
244
- },
245
- conversation_starters: {
246
- type: [String],
247
- default: [],
248
- },
249
- access_level: {
250
- type: Number,
251
- },
252
- file_ids: { type: [String], default: undefined },
253
- actions: { type: [String], default: undefined },
254
- append_current_datetime: {
255
- type: Boolean,
256
- default: false,
257
- },
258
- }, {
259
- timestamps: true,
311
+ /**
312
+ * Truncates long strings, especially base64 image data, within log messages.
313
+ *
314
+ * @param value - The value to be inspected and potentially truncated.
315
+ * @param length - The length at which to truncate the value. Default: 100.
316
+ * @returns The truncated or original value.
317
+ */
318
+ const truncateLongStrings = (value, length = 100) => {
319
+ if (typeof value === 'string') {
320
+ return value.length > length ? value.substring(0, length) + '... [truncated]' : value;
321
+ }
322
+ return value;
323
+ };
324
+ /**
325
+ * An array mapping function that truncates long strings (objects converted to JSON strings).
326
+ * @param item - The item to be condensed.
327
+ * @returns The condensed item.
328
+ */
329
+ const condenseArray = (item) => {
330
+ if (typeof item === 'string') {
331
+ return truncateLongStrings(JSON.stringify(item));
332
+ }
333
+ else if (typeof item === 'object') {
334
+ return truncateLongStrings(JSON.stringify(item));
335
+ }
336
+ return item;
337
+ };
338
+ /**
339
+ * Formats log messages for debugging purposes.
340
+ * - Truncates long strings within log messages.
341
+ * - Condenses arrays by truncating long strings and objects as strings within array items.
342
+ * - Redacts sensitive information from log messages if the log level is 'error'.
343
+ * - Converts log information object to a formatted string.
344
+ *
345
+ * @param options - The options for formatting log messages.
346
+ * @returns The formatted log message.
347
+ */
348
+ const debugTraverse = winston.format.printf(({ level, message, timestamp, ...metadata }) => {
349
+ if (!message) {
350
+ return `${timestamp} ${level}`;
351
+ }
352
+ // Type-safe version of the CJS logic: !message?.trim || typeof message !== 'string'
353
+ if (typeof message !== 'string' || !message.trim) {
354
+ return `${timestamp} ${level}: ${JSON.stringify(message)}`;
355
+ }
356
+ const msgParts = [
357
+ `${timestamp} ${level}: ${truncateLongStrings(message.trim(), DEBUG_MESSAGE_LENGTH)}`,
358
+ ];
359
+ try {
360
+ if (level !== 'debug') {
361
+ return msgParts[0];
362
+ }
363
+ if (!metadata) {
364
+ return msgParts[0];
365
+ }
366
+ // Type-safe access to SPLAT_SYMBOL using bracket notation
367
+ const metadataRecord = metadata;
368
+ const splatArray = metadataRecord[SPLAT_SYMBOL];
369
+ const debugValue = Array.isArray(splatArray) ? splatArray[0] : undefined;
370
+ if (!debugValue) {
371
+ return msgParts[0];
372
+ }
373
+ if (debugValue && Array.isArray(debugValue)) {
374
+ msgParts.push(`\n${JSON.stringify(debugValue.map(condenseArray))}`);
375
+ return msgParts.join('');
376
+ }
377
+ if (typeof debugValue !== 'object') {
378
+ msgParts.push(` ${debugValue}`);
379
+ return msgParts.join('');
380
+ }
381
+ msgParts.push('\n{');
382
+ const copy = klona(metadata);
383
+ try {
384
+ const traversal = traverse(copy);
385
+ traversal.forEach(function (value) {
386
+ var _a;
387
+ if (typeof (this === null || this === void 0 ? void 0 : this.key) === 'symbol') {
388
+ return;
389
+ }
390
+ let _parentKey = '';
391
+ const parent = this.parent;
392
+ if (typeof (parent === null || parent === void 0 ? void 0 : parent.key) !== 'symbol' && (parent === null || parent === void 0 ? void 0 : parent.key) !== undefined) {
393
+ _parentKey = String(parent.key);
394
+ }
395
+ const parentKey = `${parent && parent.notRoot ? _parentKey + '.' : ''}`;
396
+ const tabs = `${parent && parent.notRoot ? ' ' : ' '}`;
397
+ const currentKey = (_a = this === null || this === void 0 ? void 0 : this.key) !== null && _a !== void 0 ? _a : 'unknown';
398
+ if (this.isLeaf && typeof value === 'string') {
399
+ const truncatedText = truncateLongStrings(value);
400
+ msgParts.push(`\n${tabs}${parentKey}${currentKey}: ${JSON.stringify(truncatedText)},`);
401
+ }
402
+ else if (this.notLeaf && Array.isArray(value) && value.length > 0) {
403
+ const currentMessage = `\n${tabs}// ${value.length} ${String(currentKey).replace(/s$/, '')}(s)`;
404
+ this.update(currentMessage);
405
+ msgParts.push(currentMessage);
406
+ const stringifiedArray = value.map(condenseArray);
407
+ msgParts.push(`\n${tabs}${parentKey}${currentKey}: [${stringifiedArray}],`);
408
+ }
409
+ else if (this.isLeaf && typeof value === 'function') {
410
+ msgParts.push(`\n${tabs}${parentKey}${currentKey}: function,`);
411
+ }
412
+ else if (this.isLeaf) {
413
+ msgParts.push(`\n${tabs}${parentKey}${currentKey}: ${value},`);
414
+ }
415
+ });
416
+ }
417
+ catch (e) {
418
+ const errorMessage = e instanceof Error ? e.message : 'Unknown error';
419
+ msgParts.push(`\n[LOGGER TRAVERSAL ERROR] ${errorMessage}`);
420
+ }
421
+ msgParts.push('\n}');
422
+ return msgParts.join('');
423
+ }
424
+ catch (e) {
425
+ const errorMessage = e instanceof Error ? e.message : 'Unknown error';
426
+ msgParts.push(`\n[LOGGER PARSING ERROR] ${errorMessage}`);
427
+ return msgParts.join('');
428
+ }
260
429
  });
261
-
262
- const balanceSchema = new Schema({
263
- user: {
264
- type: Schema.Types.ObjectId,
265
- ref: 'User',
266
- index: true,
267
- required: true,
268
- },
269
- // 1000 tokenCredits = 1 mill ($0.001 USD)
270
- tokenCredits: {
271
- type: Number,
272
- default: 0,
273
- },
274
- // Automatic refill settings
275
- autoRefillEnabled: {
276
- type: Boolean,
277
- default: false,
278
- },
279
- refillIntervalValue: {
280
- type: Number,
281
- default: 30,
282
- },
283
- refillIntervalUnit: {
284
- type: String,
285
- enum: ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months'],
286
- default: 'days',
287
- },
288
- lastRefill: {
289
- type: Date,
290
- default: Date.now,
291
- },
292
- // amount to add on each refill
293
- refillAmount: {
294
- type: Number,
295
- default: 0,
296
- },
430
+ /**
431
+ * Truncates long string values in JSON log objects.
432
+ * Prevents outputting extremely long values (e.g., base64, blobs).
433
+ */
434
+ const jsonTruncateFormat = winston.format((info) => {
435
+ const truncateLongStrings = (str, maxLength) => str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
436
+ const seen = new WeakSet();
437
+ const truncateObject = (obj) => {
438
+ if (typeof obj !== 'object' || obj === null) {
439
+ return obj;
440
+ }
441
+ // Handle circular references - now with proper object type
442
+ if (seen.has(obj)) {
443
+ return '[Circular]';
444
+ }
445
+ seen.add(obj);
446
+ if (Array.isArray(obj)) {
447
+ return obj.map((item) => truncateObject(item));
448
+ }
449
+ // We know this is an object at this point
450
+ const objectRecord = obj;
451
+ const newObj = {};
452
+ Object.entries(objectRecord).forEach(([key, value]) => {
453
+ if (typeof value === 'string') {
454
+ newObj[key] = truncateLongStrings(value, CONSOLE_JSON_STRING_LENGTH);
455
+ }
456
+ else {
457
+ newObj[key] = truncateObject(value);
458
+ }
459
+ });
460
+ return newObj;
461
+ };
462
+ return truncateObject(info);
297
463
  });
298
464
 
299
- const bannerSchema = new Schema({
300
- bannerId: {
301
- type: String,
302
- required: true,
303
- },
304
- message: {
305
- type: String,
306
- required: true,
307
- },
308
- displayFrom: {
309
- type: Date,
310
- required: true,
311
- default: Date.now,
312
- },
313
- displayTo: {
314
- type: Date,
315
- },
316
- type: {
317
- type: String,
318
- enum: ['banner', 'popup'],
319
- default: 'banner',
320
- },
321
- isPublic: {
322
- type: Boolean,
323
- default: false,
324
- },
325
- }, { timestamps: true });
465
+ /**
466
+ * Determine the log directory in a cross-compatible way.
467
+ * Priority:
468
+ * 1. LIBRECHAT_LOG_DIR environment variable
469
+ * 2. If running within LibreChat monorepo (when cwd ends with /api), use api/logs
470
+ * 3. If api/logs exists relative to cwd, use that (for running from project root)
471
+ * 4. Otherwise, use logs directory relative to process.cwd()
472
+ *
473
+ * This avoids using __dirname which is not available in ESM modules
474
+ */
475
+ const getLogDirectory = () => {
476
+ if (process.env.LIBRECHAT_LOG_DIR) {
477
+ return process.env.LIBRECHAT_LOG_DIR;
478
+ }
479
+ const cwd = process.cwd();
480
+ // Check if we're running from within the api directory
481
+ if (cwd.endsWith('/api') || cwd.endsWith('\\api')) {
482
+ return path.join(cwd, 'logs');
483
+ }
484
+ // Check if api/logs exists relative to current directory (running from project root)
485
+ // We'll just use the path and let the file system create it if needed
486
+ const apiLogsPath = path.join(cwd, 'api', 'logs');
487
+ // For LibreChat project structure, use api/logs
488
+ // For external consumers, they should set LIBRECHAT_LOG_DIR
489
+ if (cwd.includes('LibreChat')) {
490
+ return apiLogsPath;
491
+ }
492
+ // Default to logs directory relative to current working directory
493
+ return path.join(cwd, 'logs');
494
+ };
326
495
 
327
- const categoriesSchema = new Schema({
328
- label: {
329
- type: String,
330
- required: true,
331
- unique: true,
332
- },
333
- value: {
334
- type: String,
335
- required: true,
336
- unique: true,
337
- },
496
+ const logDir$1 = getLogDirectory();
497
+ const { NODE_ENV: NODE_ENV$1, DEBUG_LOGGING: DEBUG_LOGGING$1, CONSOLE_JSON, DEBUG_CONSOLE } = process.env;
498
+ const useConsoleJson = typeof CONSOLE_JSON === 'string' && CONSOLE_JSON.toLowerCase() === 'true';
499
+ const useDebugConsole = typeof DEBUG_CONSOLE === 'string' && DEBUG_CONSOLE.toLowerCase() === 'true';
500
+ const useDebugLogging$1 = typeof DEBUG_LOGGING$1 === 'string' && DEBUG_LOGGING$1.toLowerCase() === 'true';
501
+ const levels$1 = {
502
+ error: 0,
503
+ warn: 1,
504
+ info: 2,
505
+ http: 3,
506
+ verbose: 4,
507
+ debug: 5,
508
+ activity: 6,
509
+ silly: 7,
510
+ };
511
+ winston.addColors({
512
+ info: 'green',
513
+ warn: 'italic yellow',
514
+ error: 'red',
515
+ debug: 'blue',
516
+ });
517
+ const level$1 = () => {
518
+ const env = NODE_ENV$1 || 'development';
519
+ return env === 'development' ? 'debug' : 'warn';
520
+ };
521
+ const fileFormat$1 = winston.format.combine(redactFormat(), winston.format.timestamp({ format: () => new Date().toISOString() }), winston.format.errors({ stack: true }), winston.format.splat());
522
+ const transports$1 = [
523
+ new winston.transports.DailyRotateFile({
524
+ level: 'error',
525
+ filename: `${logDir$1}/error-%DATE%.log`,
526
+ datePattern: 'YYYY-MM-DD',
527
+ zippedArchive: true,
528
+ maxSize: '20m',
529
+ maxFiles: '14d',
530
+ format: winston.format.combine(fileFormat$1, winston.format.json()),
531
+ }),
532
+ ];
533
+ if (useDebugLogging$1) {
534
+ transports$1.push(new winston.transports.DailyRotateFile({
535
+ level: 'debug',
536
+ filename: `${logDir$1}/debug-%DATE%.log`,
537
+ datePattern: 'YYYY-MM-DD',
538
+ zippedArchive: true,
539
+ maxSize: '20m',
540
+ maxFiles: '14d',
541
+ format: winston.format.combine(fileFormat$1, debugTraverse),
542
+ }));
543
+ }
544
+ const consoleFormat$1 = winston.format.combine(redactFormat(), winston.format.colorize({ all: true }), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf((info) => {
545
+ const message = `${info.timestamp} ${info.level}: ${info.message}`;
546
+ return info.level.includes('error') ? redactMessage(message) : message;
547
+ }));
548
+ let consoleLogLevel = 'info';
549
+ if (useDebugConsole) {
550
+ consoleLogLevel = 'debug';
551
+ }
552
+ // Add console transport
553
+ if (useDebugConsole) {
554
+ transports$1.push(new winston.transports.Console({
555
+ level: consoleLogLevel,
556
+ format: useConsoleJson
557
+ ? winston.format.combine(fileFormat$1, jsonTruncateFormat(), winston.format.json())
558
+ : winston.format.combine(fileFormat$1, debugTraverse),
559
+ }));
560
+ }
561
+ else if (useConsoleJson) {
562
+ transports$1.push(new winston.transports.Console({
563
+ level: consoleLogLevel,
564
+ format: winston.format.combine(fileFormat$1, jsonTruncateFormat(), winston.format.json()),
565
+ }));
566
+ }
567
+ else {
568
+ transports$1.push(new winston.transports.Console({
569
+ level: consoleLogLevel,
570
+ format: consoleFormat$1,
571
+ }));
572
+ }
573
+ // Create logger
574
+ const logger$1 = winston.createLogger({
575
+ level: level$1(),
576
+ levels: levels$1,
577
+ transports: transports$1,
338
578
  });
339
579
 
340
- const conversationTag = new Schema({
341
- tag: {
342
- type: String,
343
- index: true,
344
- },
345
- user: {
346
- type: String,
347
- index: true,
348
- },
349
- description: {
350
- type: String,
351
- index: true,
352
- },
353
- count: {
354
- type: Number,
355
- default: 0,
356
- },
357
- position: {
358
- type: Number,
359
- default: 0,
360
- index: true,
361
- },
362
- }, { timestamps: true });
363
- // Create a compound index on tag and user with unique constraint.
364
- conversationTag.index({ tag: 1, user: 1 }, { unique: true });
365
-
366
- // @ts-ignore
367
- const conversationPreset = {
368
- // endpoint: [azureOpenAI, openAI, anthropic, chatGPTBrowser]
369
- endpoint: {
370
- type: String,
371
- default: null,
372
- required: true,
373
- },
374
- endpointType: {
375
- type: String,
376
- },
377
- // for azureOpenAI, openAI, chatGPTBrowser only
378
- model: {
379
- type: String,
380
- required: false,
381
- },
382
- // for bedrock only
383
- region: {
384
- type: String,
385
- required: false,
386
- },
387
- // for azureOpenAI, openAI only
388
- chatGptLabel: {
389
- type: String,
390
- required: false,
391
- },
392
- // for google only
393
- examples: { type: [{ type: Schema.Types.Mixed }], default: undefined },
394
- modelLabel: {
395
- type: String,
396
- required: false,
397
- },
398
- promptPrefix: {
399
- type: String,
400
- required: false,
401
- },
402
- temperature: {
403
- type: Number,
404
- required: false,
405
- },
406
- top_p: {
407
- type: Number,
408
- required: false,
409
- },
410
- // for google only
411
- topP: {
412
- type: Number,
413
- required: false,
414
- },
415
- topK: {
416
- type: Number,
417
- required: false,
418
- },
419
- maxOutputTokens: {
420
- type: Number,
421
- required: false,
422
- },
423
- maxTokens: {
424
- type: Number,
425
- required: false,
426
- },
427
- presence_penalty: {
428
- type: Number,
429
- required: false,
430
- },
431
- frequency_penalty: {
432
- type: Number,
433
- required: false,
434
- },
435
- file_ids: { type: [{ type: String }], default: undefined },
436
- // deprecated
437
- resendImages: {
438
- type: Boolean,
439
- },
440
- /* Anthropic only */
441
- promptCache: {
442
- type: Boolean,
443
- },
444
- thinking: {
445
- type: Boolean,
446
- },
447
- thinkingBudget: {
448
- type: Number,
449
- },
450
- system: {
451
- type: String,
452
- },
453
- // files
454
- resendFiles: {
455
- type: Boolean,
456
- },
457
- imageDetail: {
458
- type: String,
459
- },
460
- /* agents */
461
- agent_id: {
462
- type: String,
463
- },
464
- /* assistants */
465
- assistant_id: {
466
- type: String,
467
- },
468
- instructions: {
469
- type: String,
470
- },
471
- stop: { type: [{ type: String }], default: undefined },
472
- isArchived: {
473
- type: Boolean,
474
- default: false,
475
- },
476
- /* UI Components */
477
- iconURL: {
478
- type: String,
479
- },
480
- greeting: {
481
- type: String,
482
- },
483
- spec: {
484
- type: String,
485
- },
486
- tags: {
487
- type: [String],
488
- default: [],
489
- },
490
- tools: { type: [{ type: String }], default: undefined },
491
- maxContextTokens: {
492
- type: Number,
493
- },
494
- max_tokens: {
495
- type: Number,
496
- },
497
- useResponsesApi: {
498
- type: Boolean,
499
- },
500
- /** OpenAI Responses API / Anthropic API / Google API */
501
- web_search: {
502
- type: Boolean,
503
- },
504
- disableStreaming: {
505
- type: Boolean,
506
- },
507
- fileTokenLimit: {
508
- type: Number,
509
- },
510
- /** Reasoning models only */
511
- reasoning_effort: {
512
- type: String,
580
+ /**
581
+ * Loads and maps the Cloudflare Turnstile configuration.
582
+ *
583
+ * Expected config structure:
584
+ *
585
+ * turnstile:
586
+ * siteKey: "your-site-key-here"
587
+ * options:
588
+ * language: "auto" // "auto" or an ISO 639-1 language code (e.g. en)
589
+ * size: "normal" // Options: "normal", "compact", "flexible", or "invisible"
590
+ *
591
+ * @param config - The loaded custom configuration.
592
+ * @param configDefaults - The custom configuration default values.
593
+ * @returns The mapped Turnstile configuration.
594
+ */
595
+ function loadTurnstileConfig(config, configDefaults) {
596
+ var _a, _b;
597
+ const { turnstile: customTurnstile } = config !== null && config !== void 0 ? config : {};
598
+ const { turnstile: defaults } = configDefaults;
599
+ const loadedTurnstile = removeNullishValues({
600
+ siteKey: (_a = customTurnstile === null || customTurnstile === void 0 ? void 0 : customTurnstile.siteKey) !== null && _a !== void 0 ? _a : defaults === null || defaults === void 0 ? void 0 : defaults.siteKey,
601
+ options: (_b = customTurnstile === null || customTurnstile === void 0 ? void 0 : customTurnstile.options) !== null && _b !== void 0 ? _b : defaults === null || defaults === void 0 ? void 0 : defaults.options,
602
+ });
603
+ const enabled = Boolean(loadedTurnstile.siteKey);
604
+ if (enabled) {
605
+ logger$1.debug('Turnstile is ENABLED with configuration:\n' + JSON.stringify(loadedTurnstile, null, 2));
606
+ }
607
+ else {
608
+ logger$1.debug('Turnstile is DISABLED (no siteKey provided).');
609
+ }
610
+ return loadedTurnstile;
611
+ }
612
+
613
+ const webSearchAuth = {
614
+ providers: {
615
+ serper: {
616
+ serperApiKey: 1,
617
+ },
618
+ searxng: {
619
+ searxngInstanceUrl: 1,
620
+ /** Optional (0) */
621
+ searxngApiKey: 0,
622
+ },
513
623
  },
514
- reasoning_summary: {
515
- type: String,
624
+ scrapers: {
625
+ firecrawl: {
626
+ firecrawlApiKey: 1,
627
+ /** Optional (0) */
628
+ firecrawlApiUrl: 0,
629
+ firecrawlVersion: 0,
630
+ },
631
+ serper: {
632
+ serperApiKey: 1,
633
+ },
516
634
  },
517
- /** Verbosity control */
518
- verbosity: {
519
- type: String,
635
+ rerankers: {
636
+ jina: {
637
+ jinaApiKey: 1,
638
+ /** Optional (0) */
639
+ jinaApiUrl: 0,
640
+ },
641
+ cohere: { cohereApiKey: 1 },
520
642
  },
521
643
  };
644
+ /**
645
+ * Extracts all unique API keys from the webSearchAuth configuration object
646
+ */
647
+ function getWebSearchKeys() {
648
+ const keysSet = new Set();
649
+ // Iterate through each category (providers, scrapers, rerankers)
650
+ for (const category of Object.keys(webSearchAuth)) {
651
+ const categoryObj = webSearchAuth[category];
652
+ // Iterate through each service within the category
653
+ for (const service of Object.keys(categoryObj)) {
654
+ const serviceObj = categoryObj[service];
655
+ // Extract the API keys from the service and add to set for deduplication
656
+ for (const key of Object.keys(serviceObj)) {
657
+ keysSet.add(key);
658
+ }
659
+ }
660
+ }
661
+ return Array.from(keysSet);
662
+ }
663
+ const webSearchKeys = getWebSearchKeys();
664
+ function loadWebSearchConfig(config) {
665
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
666
+ const serperApiKey = (_a = config === null || config === void 0 ? void 0 : config.serperApiKey) !== null && _a !== void 0 ? _a : '${SERPER_API_KEY}';
667
+ const searxngInstanceUrl = (_b = config === null || config === void 0 ? void 0 : config.searxngInstanceUrl) !== null && _b !== void 0 ? _b : '${SEARXNG_INSTANCE_URL}';
668
+ const searxngApiKey = (_c = config === null || config === void 0 ? void 0 : config.searxngApiKey) !== null && _c !== void 0 ? _c : '${SEARXNG_API_KEY}';
669
+ const firecrawlApiKey = (_d = config === null || config === void 0 ? void 0 : config.firecrawlApiKey) !== null && _d !== void 0 ? _d : '${FIRECRAWL_API_KEY}';
670
+ const firecrawlApiUrl = (_e = config === null || config === void 0 ? void 0 : config.firecrawlApiUrl) !== null && _e !== void 0 ? _e : '${FIRECRAWL_API_URL}';
671
+ const firecrawlVersion = (_f = config === null || config === void 0 ? void 0 : config.firecrawlVersion) !== null && _f !== void 0 ? _f : '${FIRECRAWL_VERSION}';
672
+ const jinaApiKey = (_g = config === null || config === void 0 ? void 0 : config.jinaApiKey) !== null && _g !== void 0 ? _g : '${JINA_API_KEY}';
673
+ const jinaApiUrl = (_h = config === null || config === void 0 ? void 0 : config.jinaApiUrl) !== null && _h !== void 0 ? _h : '${JINA_API_URL}';
674
+ const cohereApiKey = (_j = config === null || config === void 0 ? void 0 : config.cohereApiKey) !== null && _j !== void 0 ? _j : '${COHERE_API_KEY}';
675
+ const safeSearch = (_k = config === null || config === void 0 ? void 0 : config.safeSearch) !== null && _k !== void 0 ? _k : SafeSearchTypes.MODERATE;
676
+ return {
677
+ ...config,
678
+ safeSearch,
679
+ jinaApiKey,
680
+ jinaApiUrl,
681
+ cohereApiKey,
682
+ serperApiKey,
683
+ searxngApiKey,
684
+ firecrawlApiKey,
685
+ firecrawlApiUrl,
686
+ firecrawlVersion,
687
+ searxngInstanceUrl,
688
+ };
689
+ }
522
690
 
523
- const convoSchema = new Schema({
524
- conversationId: {
525
- type: String,
526
- unique: true,
527
- required: true,
528
- index: true,
529
- meiliIndex: true,
530
- },
531
- title: {
532
- type: String,
533
- default: 'New Chat',
534
- meiliIndex: true,
535
- },
536
- user: {
537
- type: String,
538
- index: true,
539
- meiliIndex: true,
540
- },
541
- messages: [{ type: Schema.Types.ObjectId, ref: 'Message' }],
542
- agentOptions: {
543
- type: Schema.Types.Mixed,
544
- },
545
- ...conversationPreset,
546
- agent_id: {
547
- type: String,
548
- },
549
- tags: {
550
- type: [String],
551
- default: [],
552
- meiliIndex: true,
553
- },
554
- files: {
555
- type: [String],
556
- },
557
- expiredAt: {
558
- type: Date,
559
- },
560
- }, { timestamps: true });
561
- convoSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
562
- convoSchema.index({ createdAt: 1, updatedAt: 1 });
563
- convoSchema.index({ conversationId: 1, user: 1 }, { unique: true });
691
+ /**
692
+ * Sets up Model Specs from the config (`librechat.yaml`) file.
693
+ * @param [endpoints] - The loaded custom configuration for endpoints.
694
+ * @param [modelSpecs] - The loaded custom configuration for model specs.
695
+ * @param [interfaceConfig] - The loaded interface configuration.
696
+ * @returns The processed model specs, if any.
697
+ */
698
+ function processModelSpecs(endpoints, _modelSpecs, interfaceConfig) {
699
+ var _a, _b, _c, _d;
700
+ if (!_modelSpecs) {
701
+ return undefined;
702
+ }
703
+ const list = _modelSpecs.list;
704
+ const modelSpecs = [];
705
+ const customEndpoints = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints[EModelEndpoint.custom]) !== null && _a !== void 0 ? _a : [];
706
+ if ((interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.modelSelect) !== true && ((_c = (_b = _modelSpecs.addedEndpoints) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) > 0) {
707
+ logger$1.warn(`To utilize \`addedEndpoints\`, which allows provider/model selections alongside model specs, set \`modelSelect: true\` in the interface configuration.
564
708
 
565
- const file = new Schema({
709
+ Example:
710
+ \`\`\`yaml
711
+ interface:
712
+ modelSelect: true
713
+ \`\`\`
714
+ `);
715
+ }
716
+ if (!list || list.length === 0) {
717
+ return undefined;
718
+ }
719
+ for (const spec of list) {
720
+ const currentEndpoint = (_d = spec.preset) === null || _d === void 0 ? void 0 : _d.endpoint;
721
+ if (!currentEndpoint) {
722
+ logger$1.warn('A model spec is missing the `endpoint` field within its `preset`. Skipping model spec...');
723
+ continue;
724
+ }
725
+ if (EModelEndpoint[currentEndpoint] && currentEndpoint !== EModelEndpoint.custom) {
726
+ modelSpecs.push(spec);
727
+ continue;
728
+ }
729
+ else if (currentEndpoint === EModelEndpoint.custom) {
730
+ logger$1.warn(`Model Spec with endpoint "${currentEndpoint}" is not supported. You must specify the name of the custom endpoint (case-sensitive, as defined in your config). Skipping model spec...`);
731
+ continue;
732
+ }
733
+ const normalizedName = normalizeEndpointName(currentEndpoint);
734
+ const endpoint = customEndpoints.find((customEndpoint) => normalizedName === normalizeEndpointName(customEndpoint.name));
735
+ if (!endpoint) {
736
+ logger$1.warn(`Model spec with endpoint "${currentEndpoint}" was skipped: Endpoint not found in configuration. The \`endpoint\` value must exactly match either a system-defined endpoint or a custom endpoint defined by the user.
737
+
738
+ For more information, see the documentation at https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/model_specs#endpoint`);
739
+ continue;
740
+ }
741
+ modelSpecs.push({
742
+ ...spec,
743
+ preset: {
744
+ ...spec.preset,
745
+ endpoint: normalizedName,
746
+ },
747
+ });
748
+ }
749
+ return {
750
+ ..._modelSpecs,
751
+ list: modelSpecs,
752
+ };
753
+ }
754
+
755
+ /**
756
+ * Sets up the minimum, default Assistants configuration if Azure OpenAI Assistants option is enabled.
757
+ * @returns The Assistants endpoint configuration.
758
+ */
759
+ function azureAssistantsDefaults() {
760
+ return {
761
+ capabilities: [Capabilities.tools, Capabilities.actions, Capabilities.code_interpreter],
762
+ version: defaultAssistantsVersion.azureAssistants,
763
+ };
764
+ }
765
+ /**
766
+ * Sets up the Assistants configuration from the config (`librechat.yaml`) file.
767
+ * @param config - The loaded custom configuration.
768
+ * @param assistantsEndpoint - The Assistants endpoint name.
769
+ * - The previously loaded assistants configuration from Azure OpenAI Assistants option.
770
+ * @param [prevConfig]
771
+ * @returns The Assistants endpoint configuration.
772
+ */
773
+ function assistantsConfigSetup(config, assistantsEndpoint, prevConfig = {}) {
774
+ var _a, _b, _c, _d, _e;
775
+ const assistantsConfig = (_a = config.endpoints) === null || _a === void 0 ? void 0 : _a[assistantsEndpoint];
776
+ const parsedConfig = assistantEndpointSchema.parse(assistantsConfig);
777
+ if (((_b = assistantsConfig === null || assistantsConfig === void 0 ? void 0 : assistantsConfig.supportedIds) === null || _b === void 0 ? void 0 : _b.length) && ((_c = assistantsConfig.excludedIds) === null || _c === void 0 ? void 0 : _c.length)) {
778
+ logger$1.warn(`Configuration conflict: The '${assistantsEndpoint}' endpoint has both 'supportedIds' and 'excludedIds' defined. The 'excludedIds' will be ignored.`);
779
+ }
780
+ if ((assistantsConfig === null || assistantsConfig === void 0 ? void 0 : assistantsConfig.privateAssistants) &&
781
+ (((_d = assistantsConfig.supportedIds) === null || _d === void 0 ? void 0 : _d.length) || ((_e = assistantsConfig.excludedIds) === null || _e === void 0 ? void 0 : _e.length))) {
782
+ logger$1.warn(`Configuration conflict: The '${assistantsEndpoint}' endpoint has both 'privateAssistants' and 'supportedIds' or 'excludedIds' defined. The 'supportedIds' and 'excludedIds' will be ignored.`);
783
+ }
784
+ return {
785
+ ...prevConfig,
786
+ retrievalModels: parsedConfig.retrievalModels,
787
+ disableBuilder: parsedConfig.disableBuilder,
788
+ pollIntervalMs: parsedConfig.pollIntervalMs,
789
+ supportedIds: parsedConfig.supportedIds,
790
+ capabilities: parsedConfig.capabilities,
791
+ excludedIds: parsedConfig.excludedIds,
792
+ privateAssistants: parsedConfig.privateAssistants,
793
+ timeoutMs: parsedConfig.timeoutMs,
794
+ streamRate: parsedConfig.streamRate,
795
+ titlePrompt: parsedConfig.titlePrompt,
796
+ titleMethod: parsedConfig.titleMethod,
797
+ titleModel: parsedConfig.titleModel,
798
+ titleEndpoint: parsedConfig.titleEndpoint,
799
+ titlePromptTemplate: parsedConfig.titlePromptTemplate,
800
+ };
801
+ }
802
+
803
+ /**
804
+ * Sets up the Azure OpenAI configuration from the config (`librechat.yaml`) file.
805
+ * @param config - The loaded custom configuration.
806
+ * @returns The Azure OpenAI configuration.
807
+ */
808
+ function azureConfigSetup(config) {
809
+ var _a, _b, _c;
810
+ const azureConfig = (_a = config.endpoints) === null || _a === void 0 ? void 0 : _a[EModelEndpoint.azureOpenAI];
811
+ if (!azureConfig) {
812
+ throw new Error('Azure OpenAI configuration is missing.');
813
+ }
814
+ const { groups, ...azureConfiguration } = azureConfig;
815
+ const { isValid, modelNames, modelGroupMap, groupMap, errors } = validateAzureGroups(groups);
816
+ if (!isValid) {
817
+ const errorString = errors.join('\n');
818
+ const errorMessage = 'Invalid Azure OpenAI configuration:\n' + errorString;
819
+ logger$1.error(errorMessage);
820
+ throw new Error(errorMessage);
821
+ }
822
+ const assistantModels = [];
823
+ const assistantGroups = new Set();
824
+ for (const modelName of modelNames) {
825
+ mapModelToAzureConfig({ modelName, modelGroupMap, groupMap });
826
+ const groupName = (_b = modelGroupMap === null || modelGroupMap === void 0 ? void 0 : modelGroupMap[modelName]) === null || _b === void 0 ? void 0 : _b.group;
827
+ const modelGroup = groupMap === null || groupMap === void 0 ? void 0 : groupMap[groupName];
828
+ const supportsAssistants = (modelGroup === null || modelGroup === void 0 ? void 0 : modelGroup.assistants) || ((_c = modelGroup === null || modelGroup === void 0 ? void 0 : modelGroup[modelName]) === null || _c === void 0 ? void 0 : _c.assistants);
829
+ if (supportsAssistants) {
830
+ assistantModels.push(modelName);
831
+ if (!assistantGroups.has(groupName)) {
832
+ assistantGroups.add(groupName);
833
+ }
834
+ }
835
+ }
836
+ if (azureConfiguration.assistants && assistantModels.length === 0) {
837
+ throw new Error('No Azure models are configured to support assistants. Please remove the `assistants` field or configure at least one model to support assistants.');
838
+ }
839
+ if (azureConfiguration.assistants &&
840
+ process.env.ENDPOINTS &&
841
+ !process.env.ENDPOINTS.includes(EModelEndpoint.azureAssistants)) {
842
+ logger$1.warn(`Azure Assistants are configured, but the endpoint will not be accessible as it's not included in the ENDPOINTS environment variable.
843
+ Please add the value "${EModelEndpoint.azureAssistants}" to the ENDPOINTS list if expected.`);
844
+ }
845
+ return {
846
+ errors,
847
+ isValid,
848
+ groupMap,
849
+ modelNames,
850
+ modelGroupMap,
851
+ assistantModels,
852
+ assistantGroups: Array.from(assistantGroups),
853
+ ...azureConfiguration,
854
+ };
855
+ }
856
+
857
+ /**
858
+ * Loads custom config endpoints
859
+ * @param [config]
860
+ * @param [agentsDefaults]
861
+ */
862
+ const loadEndpoints = (config, agentsDefaults) => {
863
+ var _a;
864
+ const loadedEndpoints = {};
865
+ const endpoints = config === null || config === void 0 ? void 0 : config.endpoints;
866
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints[EModelEndpoint.azureOpenAI]) {
867
+ loadedEndpoints[EModelEndpoint.azureOpenAI] = azureConfigSetup(config);
868
+ }
869
+ if ((_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints[EModelEndpoint.azureOpenAI]) === null || _a === void 0 ? void 0 : _a.assistants) {
870
+ loadedEndpoints[EModelEndpoint.azureAssistants] = azureAssistantsDefaults();
871
+ }
872
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints[EModelEndpoint.azureAssistants]) {
873
+ loadedEndpoints[EModelEndpoint.azureAssistants] = assistantsConfigSetup(config, EModelEndpoint.azureAssistants, loadedEndpoints[EModelEndpoint.azureAssistants]);
874
+ }
875
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints[EModelEndpoint.assistants]) {
876
+ loadedEndpoints[EModelEndpoint.assistants] = assistantsConfigSetup(config, EModelEndpoint.assistants, loadedEndpoints[EModelEndpoint.assistants]);
877
+ }
878
+ loadedEndpoints[EModelEndpoint.agents] = agentsConfigSetup(config, agentsDefaults);
879
+ const endpointKeys = [
880
+ EModelEndpoint.openAI,
881
+ EModelEndpoint.google,
882
+ EModelEndpoint.custom,
883
+ EModelEndpoint.bedrock,
884
+ EModelEndpoint.anthropic,
885
+ ];
886
+ endpointKeys.forEach((key) => {
887
+ const currentKey = key;
888
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints[currentKey]) {
889
+ loadedEndpoints[currentKey] = endpoints[currentKey];
890
+ }
891
+ });
892
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints.all) {
893
+ loadedEndpoints.all = endpoints.all;
894
+ }
895
+ return loadedEndpoints;
896
+ };
897
+
898
+ function loadOCRConfig(config) {
899
+ var _a, _b, _c, _d;
900
+ if (!config)
901
+ return;
902
+ const baseURL = (_a = config === null || config === void 0 ? void 0 : config.baseURL) !== null && _a !== void 0 ? _a : '';
903
+ const apiKey = (_b = config === null || config === void 0 ? void 0 : config.apiKey) !== null && _b !== void 0 ? _b : '';
904
+ const mistralModel = (_c = config === null || config === void 0 ? void 0 : config.mistralModel) !== null && _c !== void 0 ? _c : '';
905
+ return {
906
+ apiKey,
907
+ baseURL,
908
+ mistralModel,
909
+ strategy: (_d = config === null || config === void 0 ? void 0 : config.strategy) !== null && _d !== void 0 ? _d : OCRStrategy.MISTRAL_OCR,
910
+ };
911
+ }
912
+
913
+ /**
914
+ * Loads custom config and initializes app-wide variables.
915
+ * @function AppService
916
+ */
917
+ const AppService = async (params) => {
918
+ var _a, _b, _c, _d, _e, _f;
919
+ const { config, paths, systemTools } = params || {};
920
+ if (!config) {
921
+ throw new Error('Config is required');
922
+ }
923
+ const configDefaults = getConfigDefaults();
924
+ const ocr = loadOCRConfig(config.ocr);
925
+ const webSearch = loadWebSearchConfig(config.webSearch);
926
+ const memory = loadMemoryConfig(config.memory);
927
+ const filteredTools = config.filteredTools;
928
+ const includedTools = config.includedTools;
929
+ const fileStrategy = ((_a = config.fileStrategy) !== null && _a !== void 0 ? _a : configDefaults.fileStrategy);
930
+ const startBalance = process.env.START_BALANCE;
931
+ const balance = (_b = config.balance) !== null && _b !== void 0 ? _b : {
932
+ enabled: ((_c = process.env.CHECK_BALANCE) === null || _c === void 0 ? void 0 : _c.toLowerCase().trim()) === 'true',
933
+ startBalance: startBalance ? parseInt(startBalance, 10) : undefined,
934
+ };
935
+ const transactions = (_d = config.transactions) !== null && _d !== void 0 ? _d : configDefaults.transactions;
936
+ const imageOutputType = (_e = config === null || config === void 0 ? void 0 : config.imageOutputType) !== null && _e !== void 0 ? _e : configDefaults.imageOutputType;
937
+ process.env.CDN_PROVIDER = fileStrategy;
938
+ const availableTools = systemTools;
939
+ const mcpConfig = config.mcpServers || null;
940
+ const registration = (_f = config.registration) !== null && _f !== void 0 ? _f : configDefaults.registration;
941
+ const interfaceConfig = await loadDefaultInterface({ config, configDefaults });
942
+ const turnstileConfig = loadTurnstileConfig(config, configDefaults);
943
+ const speech = config.speech;
944
+ const defaultConfig = {
945
+ ocr,
946
+ paths,
947
+ config,
948
+ memory,
949
+ speech,
950
+ balance,
951
+ transactions,
952
+ mcpConfig,
953
+ webSearch,
954
+ fileStrategy,
955
+ registration,
956
+ filteredTools,
957
+ includedTools,
958
+ availableTools,
959
+ imageOutputType,
960
+ interfaceConfig,
961
+ turnstileConfig,
962
+ fileStrategies: config.fileStrategies,
963
+ };
964
+ const agentsDefaults = agentsConfigSetup(config);
965
+ if (!Object.keys(config).length) {
966
+ const appConfig = {
967
+ ...defaultConfig,
968
+ endpoints: {
969
+ [EModelEndpoint.agents]: agentsDefaults,
970
+ },
971
+ };
972
+ return appConfig;
973
+ }
974
+ const loadedEndpoints = loadEndpoints(config, agentsDefaults);
975
+ const appConfig = {
976
+ ...defaultConfig,
977
+ fileConfig: config === null || config === void 0 ? void 0 : config.fileConfig,
978
+ secureImageLinks: config === null || config === void 0 ? void 0 : config.secureImageLinks,
979
+ modelSpecs: processModelSpecs(config === null || config === void 0 ? void 0 : config.endpoints, config.modelSpecs, interfaceConfig),
980
+ endpoints: loadedEndpoints,
981
+ };
982
+ return appConfig;
983
+ };
984
+
985
+ /**
986
+ * Common role combinations
987
+ */
988
+ var RoleBits;
989
+ (function (RoleBits) {
990
+ /** 0001 = 1 */
991
+ RoleBits[RoleBits["VIEWER"] = PermissionBits.VIEW] = "VIEWER";
992
+ /** 0011 = 3 */
993
+ RoleBits[RoleBits["EDITOR"] = PermissionBits.VIEW | PermissionBits.EDIT] = "EDITOR";
994
+ /** 0111 = 7 */
995
+ RoleBits[RoleBits["MANAGER"] = PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE] = "MANAGER";
996
+ /** 1111 = 15 */
997
+ RoleBits[RoleBits["OWNER"] = PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE] = "OWNER";
998
+ })(RoleBits || (RoleBits = {}));
999
+
1000
+ async function signPayload({ payload, secret, expirationTime, }) {
1001
+ return jwt.sign(payload, secret, { expiresIn: expirationTime });
1002
+ }
1003
+ async function hashToken(str) {
1004
+ const data = new TextEncoder().encode(str);
1005
+ const hashBuffer = await webcrypto.subtle.digest('SHA-256', data);
1006
+ return Buffer.from(hashBuffer).toString('hex');
1007
+ }
1008
+
1009
+ // Define the Auth sub-schema with type-safety.
1010
+ const AuthSchema = new Schema({
1011
+ authorization_type: { type: String },
1012
+ custom_auth_header: { type: String },
1013
+ type: { type: String, enum: ['service_http', 'oauth', 'none'] },
1014
+ authorization_content_type: { type: String },
1015
+ authorization_url: { type: String },
1016
+ client_url: { type: String },
1017
+ scope: { type: String },
1018
+ token_exchange_method: { type: String, enum: ['default_post', 'basic_auth_header', null] },
1019
+ }, { _id: false });
1020
+ const Action = new Schema({
566
1021
  user: {
567
1022
  type: mongoose.Schema.Types.ObjectId,
568
1023
  ref: 'User',
569
1024
  index: true,
570
1025
  required: true,
571
1026
  },
572
- conversationId: {
573
- type: String,
574
- ref: 'Conversation',
575
- index: true,
576
- },
577
- file_id: {
1027
+ action_id: {
578
1028
  type: String,
579
1029
  index: true,
580
1030
  required: true,
581
1031
  },
582
- temp_file_id: {
583
- type: String,
584
- },
585
- bytes: {
586
- type: Number,
587
- required: true,
588
- },
589
- filename: {
590
- type: String,
591
- required: true,
592
- },
593
- filepath: {
594
- type: String,
595
- required: true,
596
- },
597
- object: {
598
- type: String,
599
- required: true,
600
- default: 'file',
601
- },
602
- embedded: {
603
- type: Boolean,
604
- },
605
1032
  type: {
606
1033
  type: String,
607
- required: true,
608
- },
609
- text: {
610
- type: String,
611
- },
612
- context: {
613
- type: String,
614
- },
615
- usage: {
616
- type: Number,
617
- required: true,
618
- default: 0,
619
- },
620
- source: {
621
- type: String,
622
- default: FileSources.local,
623
- },
624
- model: {
625
- type: String,
1034
+ default: 'action_prototype',
626
1035
  },
627
- width: Number,
628
- height: Number,
1036
+ settings: Schema.Types.Mixed,
1037
+ agent_id: String,
1038
+ assistant_id: String,
629
1039
  metadata: {
630
- fileIdentifier: String,
631
- },
632
- expiresAt: {
633
- type: Date,
634
- expires: 3600, // 1 hour in seconds
635
- },
636
- }, {
637
- timestamps: true,
638
- });
639
- file.index({ createdAt: 1, updatedAt: 1 });
640
-
641
- const keySchema = new Schema({
642
- userId: {
643
- type: mongoose.Schema.Types.ObjectId,
644
- ref: 'User',
645
- required: true,
646
- },
647
- name: {
648
- type: String,
649
- required: true,
650
- },
651
- value: {
652
- type: String,
653
- required: true,
654
- },
655
- expiresAt: {
656
- type: Date,
1040
+ api_key: String,
1041
+ auth: AuthSchema,
1042
+ domain: {
1043
+ type: String,
1044
+ required: true,
1045
+ },
1046
+ privacy_policy_url: String,
1047
+ raw_spec: String,
1048
+ oauth_client_id: String,
1049
+ oauth_client_secret: String,
657
1050
  },
658
1051
  });
659
- keySchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
660
1052
 
661
- const messageSchema = new Schema({
662
- messageId: {
1053
+ const agentSchema = new Schema({
1054
+ id: {
663
1055
  type: String,
1056
+ index: true,
664
1057
  unique: true,
665
1058
  required: true,
666
- index: true,
667
- meiliIndex: true,
668
1059
  },
669
- conversationId: {
1060
+ name: {
670
1061
  type: String,
671
- index: true,
672
- required: true,
673
- meiliIndex: true,
674
1062
  },
675
- user: {
1063
+ description: {
676
1064
  type: String,
677
- index: true,
678
- required: true,
679
- default: null,
680
- meiliIndex: true,
681
1065
  },
682
- model: {
1066
+ instructions: {
683
1067
  type: String,
684
- default: null,
685
1068
  },
686
- endpoint: {
687
- type: String,
1069
+ avatar: {
1070
+ type: Schema.Types.Mixed,
1071
+ default: undefined,
688
1072
  },
689
- conversationSignature: {
1073
+ provider: {
690
1074
  type: String,
1075
+ required: true,
691
1076
  },
692
- clientId: {
1077
+ model: {
693
1078
  type: String,
1079
+ required: true,
694
1080
  },
695
- invocationId: {
696
- type: Number,
1081
+ model_parameters: {
1082
+ type: Object,
697
1083
  },
698
- parentMessageId: {
1084
+ artifacts: {
699
1085
  type: String,
700
1086
  },
701
- tokenCount: {
1087
+ access_level: {
702
1088
  type: Number,
703
1089
  },
704
- summaryTokenCount: {
1090
+ recursion_limit: {
705
1091
  type: Number,
706
1092
  },
707
- sender: {
708
- type: String,
709
- meiliIndex: true,
1093
+ tools: {
1094
+ type: [String],
1095
+ default: undefined,
710
1096
  },
711
- text: {
712
- type: String,
713
- meiliIndex: true,
1097
+ tool_kwargs: {
1098
+ type: [{ type: Schema.Types.Mixed }],
714
1099
  },
715
- summary: {
1100
+ actions: {
1101
+ type: [String],
1102
+ default: undefined,
1103
+ },
1104
+ author: {
1105
+ type: Schema.Types.ObjectId,
1106
+ ref: 'User',
1107
+ required: true,
1108
+ },
1109
+ authorName: {
716
1110
  type: String,
1111
+ default: undefined,
717
1112
  },
718
- isCreatedByUser: {
1113
+ hide_sequential_outputs: {
719
1114
  type: Boolean,
720
- required: true,
721
- default: false,
722
1115
  },
723
- unfinished: {
1116
+ end_after_tools: {
724
1117
  type: Boolean,
725
- default: false,
726
1118
  },
727
- error: {
1119
+ /** @deprecated Use edges instead */
1120
+ agent_ids: {
1121
+ type: [String],
1122
+ },
1123
+ edges: {
1124
+ type: [{ type: Schema.Types.Mixed }],
1125
+ default: [],
1126
+ },
1127
+ isCollaborative: {
728
1128
  type: Boolean,
729
- default: false,
1129
+ default: undefined,
730
1130
  },
731
- finish_reason: {
1131
+ conversation_starters: {
1132
+ type: [String],
1133
+ default: [],
1134
+ },
1135
+ tool_resources: {
1136
+ type: Schema.Types.Mixed,
1137
+ default: {},
1138
+ },
1139
+ projectIds: {
1140
+ type: [Schema.Types.ObjectId],
1141
+ ref: 'Project',
1142
+ index: true,
1143
+ },
1144
+ versions: {
1145
+ type: [Schema.Types.Mixed],
1146
+ default: [],
1147
+ },
1148
+ category: {
732
1149
  type: String,
1150
+ trim: true,
1151
+ index: true,
1152
+ default: 'general',
733
1153
  },
734
- feedback: {
735
- type: {
736
- rating: {
737
- type: String,
738
- enum: ['thumbsUp', 'thumbsDown'],
739
- required: true,
740
- },
741
- tag: {
742
- type: mongoose.Schema.Types.Mixed,
743
- required: false,
744
- },
745
- text: {
746
- type: String,
747
- required: false,
748
- },
749
- },
1154
+ support_contact: {
1155
+ type: Schema.Types.Mixed,
750
1156
  default: undefined,
751
- required: false,
752
1157
  },
753
- _meiliIndex: {
1158
+ is_promoted: {
754
1159
  type: Boolean,
755
- required: false,
756
- select: false,
757
1160
  default: false,
1161
+ index: true,
758
1162
  },
759
- files: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
760
- plugin: {
761
- type: {
762
- latest: {
763
- type: String,
764
- required: false,
765
- },
766
- inputs: {
767
- type: [mongoose.Schema.Types.Mixed],
768
- required: false,
769
- default: undefined,
770
- },
771
- outputs: {
772
- type: String,
773
- required: false,
774
- },
775
- },
776
- default: undefined,
1163
+ }, {
1164
+ timestamps: true,
1165
+ });
1166
+ agentSchema.index({ updatedAt: -1, _id: 1 });
1167
+
1168
+ const agentCategorySchema = new Schema({
1169
+ value: {
1170
+ type: String,
1171
+ required: true,
1172
+ unique: true,
1173
+ trim: true,
1174
+ lowercase: true,
1175
+ index: true,
777
1176
  },
778
- plugins: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
779
- content: {
780
- type: [{ type: mongoose.Schema.Types.Mixed }],
781
- default: undefined,
782
- meiliIndex: true,
1177
+ label: {
1178
+ type: String,
1179
+ required: true,
1180
+ trim: true,
783
1181
  },
784
- thread_id: {
1182
+ description: {
785
1183
  type: String,
1184
+ trim: true,
1185
+ default: '',
786
1186
  },
787
- /* frontend components */
788
- iconURL: {
1187
+ order: {
1188
+ type: Number,
1189
+ default: 0,
1190
+ index: true,
1191
+ },
1192
+ isActive: {
1193
+ type: Boolean,
1194
+ default: true,
1195
+ index: true,
1196
+ },
1197
+ custom: {
1198
+ type: Boolean,
1199
+ default: false,
1200
+ },
1201
+ }, {
1202
+ timestamps: true,
1203
+ });
1204
+ agentCategorySchema.index({ isActive: 1, order: 1 });
1205
+ agentCategorySchema.index({ order: 1, label: 1 });
1206
+
1207
+ const assistantSchema = new Schema({
1208
+ user: {
1209
+ type: Schema.Types.ObjectId,
1210
+ ref: 'User',
1211
+ required: true,
1212
+ },
1213
+ assistant_id: {
789
1214
  type: String,
1215
+ index: true,
1216
+ required: true,
790
1217
  },
791
- attachments: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
792
- /*
793
- attachments: {
794
- type: [
795
- {
796
- file_id: String,
797
- filename: String,
798
- filepath: String,
799
- expiresAt: Date,
800
- width: Number,
801
- height: Number,
802
- type: String,
803
- conversationId: String,
804
- messageId: {
805
- type: String,
806
- required: true,
807
- },
808
- toolCallId: String,
809
- },
810
- ],
811
- default: undefined,
1218
+ avatar: {
1219
+ type: Schema.Types.Mixed,
1220
+ default: undefined,
1221
+ },
1222
+ conversation_starters: {
1223
+ type: [String],
1224
+ default: [],
1225
+ },
1226
+ access_level: {
1227
+ type: Number,
1228
+ },
1229
+ file_ids: { type: [String], default: undefined },
1230
+ actions: { type: [String], default: undefined },
1231
+ append_current_datetime: {
1232
+ type: Boolean,
1233
+ default: false,
1234
+ },
1235
+ }, {
1236
+ timestamps: true,
1237
+ });
1238
+
1239
+ const balanceSchema = new Schema({
1240
+ user: {
1241
+ type: Schema.Types.ObjectId,
1242
+ ref: 'User',
1243
+ index: true,
1244
+ required: true,
812
1245
  },
813
- */
814
- expiredAt: {
1246
+ // 1000 tokenCredits = 1 mill ($0.001 USD)
1247
+ tokenCredits: {
1248
+ type: Number,
1249
+ default: 0,
1250
+ },
1251
+ // Automatic refill settings
1252
+ autoRefillEnabled: {
1253
+ type: Boolean,
1254
+ default: false,
1255
+ },
1256
+ refillIntervalValue: {
1257
+ type: Number,
1258
+ default: 30,
1259
+ },
1260
+ refillIntervalUnit: {
1261
+ type: String,
1262
+ enum: ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months'],
1263
+ default: 'days',
1264
+ },
1265
+ lastRefill: {
815
1266
  type: Date,
1267
+ default: Date.now,
816
1268
  },
817
- }, { timestamps: true });
818
- messageSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
819
- messageSchema.index({ createdAt: 1 });
820
- messageSchema.index({ messageId: 1, user: 1 }, { unique: true });
1269
+ // amount to add on each refill
1270
+ refillAmount: {
1271
+ type: Number,
1272
+ default: 0,
1273
+ },
1274
+ });
821
1275
 
822
- const pluginAuthSchema = new Schema({
823
- authField: {
1276
+ const bannerSchema = new Schema({
1277
+ bannerId: {
824
1278
  type: String,
825
1279
  required: true,
826
1280
  },
827
- value: {
1281
+ message: {
828
1282
  type: String,
829
1283
  required: true,
830
1284
  },
831
- userId: {
832
- type: String,
1285
+ displayFrom: {
1286
+ type: Date,
833
1287
  required: true,
1288
+ default: Date.now,
834
1289
  },
835
- pluginKey: {
1290
+ displayTo: {
1291
+ type: Date,
1292
+ },
1293
+ type: {
836
1294
  type: String,
1295
+ enum: ['banner', 'popup'],
1296
+ default: 'banner',
1297
+ },
1298
+ isPublic: {
1299
+ type: Boolean,
1300
+ default: false,
837
1301
  },
838
1302
  }, { timestamps: true });
839
1303
 
840
- const presetSchema = new Schema({
841
- presetId: {
1304
+ const categoriesSchema = new Schema({
1305
+ label: {
842
1306
  type: String,
1307
+ required: true,
843
1308
  unique: true,
1309
+ },
1310
+ value: {
1311
+ type: String,
844
1312
  required: true,
845
- index: true,
1313
+ unique: true,
846
1314
  },
847
- title: {
1315
+ });
1316
+
1317
+ const conversationTag = new Schema({
1318
+ tag: {
848
1319
  type: String,
849
- default: 'New Chat',
850
- meiliIndex: true,
1320
+ index: true,
851
1321
  },
852
1322
  user: {
853
1323
  type: String,
854
- default: null,
1324
+ index: true,
855
1325
  },
856
- defaultPreset: {
857
- type: Boolean,
1326
+ description: {
1327
+ type: String,
1328
+ index: true,
858
1329
  },
859
- order: {
1330
+ count: {
860
1331
  type: Number,
1332
+ default: 0,
861
1333
  },
862
- ...conversationPreset,
863
- agentOptions: {
864
- type: mongoose.Schema.Types.Mixed,
865
- default: null,
1334
+ position: {
1335
+ type: Number,
1336
+ default: 0,
1337
+ index: true,
866
1338
  },
867
1339
  }, { timestamps: true });
1340
+ // Create a compound index on tag and user with unique constraint.
1341
+ conversationTag.index({ tag: 1, user: 1 }, { unique: true });
868
1342
 
869
- const projectSchema = new Schema({
870
- name: {
1343
+ // @ts-ignore
1344
+ const conversationPreset = {
1345
+ // endpoint: [azureOpenAI, openAI, anthropic, chatGPTBrowser]
1346
+ endpoint: {
871
1347
  type: String,
1348
+ default: null,
872
1349
  required: true,
873
- index: true,
874
- },
875
- promptGroupIds: {
876
- type: [Schema.Types.ObjectId],
877
- ref: 'PromptGroup',
878
- default: [],
879
1350
  },
880
- agentIds: {
881
- type: [String],
882
- ref: 'Agent',
883
- default: [],
1351
+ endpointType: {
1352
+ type: String,
884
1353
  },
885
- }, {
886
- timestamps: true,
887
- });
888
-
889
- const promptSchema = new Schema({
890
- groupId: {
891
- type: Schema.Types.ObjectId,
892
- ref: 'PromptGroup',
893
- required: true,
894
- index: true,
1354
+ // for azureOpenAI, openAI, chatGPTBrowser only
1355
+ model: {
1356
+ type: String,
1357
+ required: false,
895
1358
  },
896
- author: {
897
- type: Schema.Types.ObjectId,
898
- ref: 'User',
899
- required: true,
1359
+ // for bedrock only
1360
+ region: {
1361
+ type: String,
1362
+ required: false,
900
1363
  },
901
- prompt: {
1364
+ // for azureOpenAI, openAI only
1365
+ chatGptLabel: {
902
1366
  type: String,
903
- required: true,
1367
+ required: false,
904
1368
  },
905
- type: {
1369
+ // for google only
1370
+ examples: { type: [{ type: Schema.Types.Mixed }], default: undefined },
1371
+ modelLabel: {
906
1372
  type: String,
907
- enum: ['text', 'chat'],
908
- required: true,
1373
+ required: false,
909
1374
  },
910
- }, {
911
- timestamps: true,
912
- });
913
- promptSchema.index({ createdAt: 1, updatedAt: 1 });
914
-
915
- const promptGroupSchema = new Schema({
916
- name: {
1375
+ promptPrefix: {
917
1376
  type: String,
918
- required: true,
919
- index: true,
1377
+ required: false,
920
1378
  },
921
- numberOfGenerations: {
1379
+ temperature: {
922
1380
  type: Number,
923
- default: 0,
1381
+ required: false,
924
1382
  },
925
- oneliner: {
926
- type: String,
927
- default: '',
1383
+ top_p: {
1384
+ type: Number,
1385
+ required: false,
928
1386
  },
929
- category: {
930
- type: String,
931
- default: '',
932
- index: true,
1387
+ // for google only
1388
+ topP: {
1389
+ type: Number,
1390
+ required: false,
933
1391
  },
934
- projectIds: {
935
- type: [Schema.Types.ObjectId],
936
- ref: 'Project',
937
- index: true,
938
- default: [],
1392
+ topK: {
1393
+ type: Number,
1394
+ required: false,
939
1395
  },
940
- productionId: {
941
- type: Schema.Types.ObjectId,
942
- ref: 'Prompt',
943
- required: true,
944
- index: true,
1396
+ maxOutputTokens: {
1397
+ type: Number,
1398
+ required: false,
945
1399
  },
946
- author: {
947
- type: Schema.Types.ObjectId,
948
- ref: 'User',
949
- required: true,
950
- index: true,
1400
+ maxTokens: {
1401
+ type: Number,
1402
+ required: false,
951
1403
  },
952
- authorName: {
1404
+ presence_penalty: {
1405
+ type: Number,
1406
+ required: false,
1407
+ },
1408
+ frequency_penalty: {
1409
+ type: Number,
1410
+ required: false,
1411
+ },
1412
+ file_ids: { type: [{ type: String }], default: undefined },
1413
+ // deprecated
1414
+ resendImages: {
1415
+ type: Boolean,
1416
+ },
1417
+ /* Anthropic only */
1418
+ promptCache: {
1419
+ type: Boolean,
1420
+ },
1421
+ thinking: {
1422
+ type: Boolean,
1423
+ },
1424
+ thinkingBudget: {
1425
+ type: Number,
1426
+ },
1427
+ system: {
953
1428
  type: String,
954
- required: true,
955
1429
  },
956
- command: {
1430
+ // files
1431
+ resendFiles: {
1432
+ type: Boolean,
1433
+ },
1434
+ imageDetail: {
957
1435
  type: String,
958
- index: true,
959
- validate: {
960
- validator: function (v) {
961
- return v === undefined || v === null || v === '' || /^[a-z0-9-]+$/.test(v);
962
- },
963
- message: (props) => { var _a; return `${(_a = props === null || props === void 0 ? void 0 : props.value) !== null && _a !== void 0 ? _a : 'Value'} is not a valid command. Only lowercase alphanumeric characters and hyphens are allowed.`; },
964
- },
965
- maxlength: [
966
- Constants.COMMANDS_MAX_LENGTH,
967
- `Command cannot be longer than ${Constants.COMMANDS_MAX_LENGTH} characters`,
968
- ],
969
- }, // Casting here bypasses the type error for the command field.
970
- }, {
971
- timestamps: true,
972
- });
973
- promptGroupSchema.index({ createdAt: 1, updatedAt: 1 });
974
-
975
- /**
976
- * Uses a sub-schema for permissions. Notice we disable `_id` for this subdocument.
977
- */
978
- const rolePermissionsSchema = new Schema({
979
- [PermissionTypes.BOOKMARKS]: {
980
- [Permissions.USE]: { type: Boolean },
981
1436
  },
982
- [PermissionTypes.PROMPTS]: {
983
- [Permissions.SHARED_GLOBAL]: { type: Boolean },
984
- [Permissions.USE]: { type: Boolean },
985
- [Permissions.CREATE]: { type: Boolean },
1437
+ /* agents */
1438
+ agent_id: {
1439
+ type: String,
986
1440
  },
987
- [PermissionTypes.MEMORIES]: {
988
- [Permissions.USE]: { type: Boolean },
989
- [Permissions.CREATE]: { type: Boolean },
990
- [Permissions.UPDATE]: { type: Boolean },
991
- [Permissions.READ]: { type: Boolean },
992
- [Permissions.OPT_OUT]: { type: Boolean },
1441
+ /* assistants */
1442
+ assistant_id: {
1443
+ type: String,
993
1444
  },
994
- [PermissionTypes.AGENTS]: {
995
- [Permissions.SHARED_GLOBAL]: { type: Boolean },
996
- [Permissions.USE]: { type: Boolean },
997
- [Permissions.CREATE]: { type: Boolean },
1445
+ instructions: {
1446
+ type: String,
998
1447
  },
999
- [PermissionTypes.MULTI_CONVO]: {
1000
- [Permissions.USE]: { type: Boolean },
1448
+ stop: { type: [{ type: String }], default: undefined },
1449
+ isArchived: {
1450
+ type: Boolean,
1451
+ default: false,
1001
1452
  },
1002
- [PermissionTypes.TEMPORARY_CHAT]: {
1003
- [Permissions.USE]: { type: Boolean },
1453
+ /* UI Components */
1454
+ iconURL: {
1455
+ type: String,
1004
1456
  },
1005
- [PermissionTypes.RUN_CODE]: {
1006
- [Permissions.USE]: { type: Boolean },
1457
+ greeting: {
1458
+ type: String,
1007
1459
  },
1008
- [PermissionTypes.WEB_SEARCH]: {
1009
- [Permissions.USE]: { type: Boolean },
1460
+ spec: {
1461
+ type: String,
1010
1462
  },
1011
- [PermissionTypes.PEOPLE_PICKER]: {
1012
- [Permissions.VIEW_USERS]: { type: Boolean },
1013
- [Permissions.VIEW_GROUPS]: { type: Boolean },
1014
- [Permissions.VIEW_ROLES]: { type: Boolean },
1463
+ tags: {
1464
+ type: [String],
1465
+ default: [],
1015
1466
  },
1016
- [PermissionTypes.MARKETPLACE]: {
1017
- [Permissions.USE]: { type: Boolean },
1467
+ tools: { type: [{ type: String }], default: undefined },
1468
+ maxContextTokens: {
1469
+ type: Number,
1018
1470
  },
1019
- [PermissionTypes.FILE_SEARCH]: {
1020
- [Permissions.USE]: { type: Boolean },
1471
+ max_tokens: {
1472
+ type: Number,
1021
1473
  },
1022
- [PermissionTypes.FILE_CITATIONS]: {
1023
- [Permissions.USE]: { type: Boolean },
1474
+ useResponsesApi: {
1475
+ type: Boolean,
1024
1476
  },
1025
- }, { _id: false });
1026
- const roleSchema = new Schema({
1027
- name: { type: String, required: true, unique: true, index: true },
1028
- permissions: {
1029
- type: rolePermissionsSchema,
1477
+ /** OpenAI Responses API / Anthropic API / Google API */
1478
+ web_search: {
1479
+ type: Boolean,
1030
1480
  },
1031
- });
1032
-
1033
- const sessionSchema = new Schema({
1034
- refreshTokenHash: {
1481
+ disableStreaming: {
1482
+ type: Boolean,
1483
+ },
1484
+ fileTokenLimit: {
1485
+ type: Number,
1486
+ },
1487
+ /** Reasoning models only */
1488
+ reasoning_effort: {
1035
1489
  type: String,
1036
- required: true,
1037
1490
  },
1038
- expiration: {
1039
- type: Date,
1040
- required: true,
1041
- expires: 0,
1491
+ reasoning_summary: {
1492
+ type: String,
1042
1493
  },
1043
- user: {
1044
- type: mongoose.Schema.Types.ObjectId,
1045
- ref: 'User',
1046
- required: true,
1494
+ /** Verbosity control */
1495
+ verbosity: {
1496
+ type: String,
1047
1497
  },
1048
- });
1498
+ };
1049
1499
 
1050
- const shareSchema = new Schema({
1500
+ const convoSchema = new Schema({
1051
1501
  conversationId: {
1052
1502
  type: String,
1503
+ unique: true,
1053
1504
  required: true,
1505
+ index: true,
1506
+ meiliIndex: true,
1054
1507
  },
1055
1508
  title: {
1056
1509
  type: String,
1057
- index: true,
1510
+ default: 'New Chat',
1511
+ meiliIndex: true,
1058
1512
  },
1059
1513
  user: {
1060
1514
  type: String,
1061
1515
  index: true,
1516
+ meiliIndex: true,
1062
1517
  },
1063
- messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
1064
- shareId: {
1518
+ messages: [{ type: Schema.Types.ObjectId, ref: 'Message' }],
1519
+ agentOptions: {
1520
+ type: Schema.Types.Mixed,
1521
+ },
1522
+ ...conversationPreset,
1523
+ agent_id: {
1065
1524
  type: String,
1066
- index: true,
1067
1525
  },
1068
- isPublic: {
1069
- type: Boolean,
1070
- default: true,
1526
+ tags: {
1527
+ type: [String],
1528
+ default: [],
1529
+ meiliIndex: true,
1530
+ },
1531
+ files: {
1532
+ type: [String],
1533
+ },
1534
+ expiredAt: {
1535
+ type: Date,
1071
1536
  },
1072
1537
  }, { timestamps: true });
1538
+ convoSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
1539
+ convoSchema.index({ createdAt: 1, updatedAt: 1 });
1540
+ convoSchema.index({ conversationId: 1, user: 1 }, { unique: true });
1073
1541
 
1074
- const tokenSchema = new Schema({
1075
- userId: {
1076
- type: Schema.Types.ObjectId,
1542
+ const file = new Schema({
1543
+ user: {
1544
+ type: mongoose.Schema.Types.ObjectId,
1545
+ ref: 'User',
1546
+ index: true,
1077
1547
  required: true,
1078
- ref: 'user',
1079
1548
  },
1080
- email: {
1549
+ conversationId: {
1081
1550
  type: String,
1551
+ ref: 'Conversation',
1552
+ index: true,
1082
1553
  },
1083
- type: {
1554
+ file_id: {
1084
1555
  type: String,
1556
+ index: true,
1557
+ required: true,
1085
1558
  },
1086
- identifier: {
1559
+ temp_file_id: {
1087
1560
  type: String,
1088
1561
  },
1089
- token: {
1562
+ bytes: {
1563
+ type: Number,
1564
+ required: true,
1565
+ },
1566
+ filename: {
1090
1567
  type: String,
1091
1568
  required: true,
1092
1569
  },
1093
- createdAt: {
1094
- type: Date,
1570
+ filepath: {
1571
+ type: String,
1095
1572
  required: true,
1096
- default: Date.now,
1097
1573
  },
1098
- expiresAt: {
1099
- type: Date,
1574
+ object: {
1575
+ type: String,
1100
1576
  required: true,
1577
+ default: 'file',
1101
1578
  },
1102
- metadata: {
1103
- type: Map,
1104
- of: Schema.Types.Mixed,
1579
+ embedded: {
1580
+ type: Boolean,
1105
1581
  },
1106
- });
1107
- tokenSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
1108
-
1109
- const toolCallSchema = new Schema({
1110
- conversationId: {
1582
+ type: {
1111
1583
  type: String,
1112
1584
  required: true,
1113
1585
  },
1114
- messageId: {
1586
+ text: {
1115
1587
  type: String,
1116
- required: true,
1117
1588
  },
1118
- toolId: {
1589
+ context: {
1119
1590
  type: String,
1120
- required: true,
1121
1591
  },
1122
- user: {
1123
- type: mongoose.Schema.Types.ObjectId,
1124
- ref: 'User',
1592
+ usage: {
1593
+ type: Number,
1125
1594
  required: true,
1595
+ default: 0,
1126
1596
  },
1127
- result: {
1128
- type: mongoose.Schema.Types.Mixed,
1597
+ source: {
1598
+ type: String,
1599
+ default: FileSources.local,
1129
1600
  },
1130
- attachments: {
1131
- type: mongoose.Schema.Types.Mixed,
1601
+ model: {
1602
+ type: String,
1132
1603
  },
1133
- blockIndex: {
1134
- type: Number,
1604
+ width: Number,
1605
+ height: Number,
1606
+ metadata: {
1607
+ fileIdentifier: String,
1135
1608
  },
1136
- partIndex: {
1137
- type: Number,
1609
+ expiresAt: {
1610
+ type: Date,
1611
+ expires: 3600, // 1 hour in seconds
1138
1612
  },
1139
- }, { timestamps: true });
1140
- toolCallSchema.index({ messageId: 1, user: 1 });
1141
- toolCallSchema.index({ conversationId: 1, user: 1 });
1613
+ }, {
1614
+ timestamps: true,
1615
+ });
1616
+ file.index({ createdAt: 1, updatedAt: 1 });
1142
1617
 
1143
- const transactionSchema = new Schema({
1144
- user: {
1618
+ const keySchema = new Schema({
1619
+ userId: {
1145
1620
  type: mongoose.Schema.Types.ObjectId,
1146
1621
  ref: 'User',
1147
- index: true,
1148
1622
  required: true,
1149
1623
  },
1624
+ name: {
1625
+ type: String,
1626
+ required: true,
1627
+ },
1628
+ value: {
1629
+ type: String,
1630
+ required: true,
1631
+ },
1632
+ expiresAt: {
1633
+ type: Date,
1634
+ },
1635
+ });
1636
+ keySchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
1637
+
1638
+ const messageSchema = new Schema({
1639
+ messageId: {
1640
+ type: String,
1641
+ unique: true,
1642
+ required: true,
1643
+ index: true,
1644
+ meiliIndex: true,
1645
+ },
1150
1646
  conversationId: {
1151
1647
  type: String,
1152
- ref: 'Conversation',
1153
1648
  index: true,
1649
+ required: true,
1650
+ meiliIndex: true,
1154
1651
  },
1155
- tokenType: {
1652
+ user: {
1156
1653
  type: String,
1157
- enum: ['prompt', 'completion', 'credits'],
1654
+ index: true,
1158
1655
  required: true,
1656
+ default: null,
1657
+ meiliIndex: true,
1658
+ },
1659
+ model: {
1660
+ type: String,
1661
+ default: null,
1159
1662
  },
1160
- model: {
1663
+ endpoint: {
1161
1664
  type: String,
1162
1665
  },
1163
- context: {
1666
+ conversationSignature: {
1164
1667
  type: String,
1165
1668
  },
1166
- valueKey: {
1669
+ clientId: {
1167
1670
  type: String,
1168
1671
  },
1169
- rate: Number,
1170
- rawAmount: Number,
1171
- tokenValue: Number,
1172
- inputTokens: { type: Number },
1173
- writeTokens: { type: Number },
1174
- readTokens: { type: Number },
1175
- }, {
1176
- timestamps: true,
1177
- });
1178
-
1179
- // Session sub-schema
1180
- const SessionSchema = new Schema({
1181
- refreshToken: {
1672
+ invocationId: {
1673
+ type: Number,
1674
+ },
1675
+ parentMessageId: {
1182
1676
  type: String,
1183
- default: '',
1184
1677
  },
1185
- }, { _id: false });
1186
- // Backup code sub-schema
1187
- const BackupCodeSchema = new Schema({
1188
- codeHash: { type: String, required: true },
1189
- used: { type: Boolean, default: false },
1190
- usedAt: { type: Date, default: null },
1191
- }, { _id: false });
1192
- const userSchema = new Schema({
1193
- name: {
1678
+ tokenCount: {
1679
+ type: Number,
1680
+ },
1681
+ summaryTokenCount: {
1682
+ type: Number,
1683
+ },
1684
+ sender: {
1194
1685
  type: String,
1686
+ meiliIndex: true,
1195
1687
  },
1196
- username: {
1688
+ text: {
1197
1689
  type: String,
1198
- lowercase: true,
1199
- default: '',
1690
+ meiliIndex: true,
1200
1691
  },
1201
- email: {
1692
+ summary: {
1202
1693
  type: String,
1203
- required: [true, "can't be blank"],
1204
- lowercase: true,
1205
- unique: true,
1206
- match: [/\S+@\S+\.\S+/, 'is invalid'],
1207
- index: true,
1208
1694
  },
1209
- emailVerified: {
1695
+ isCreatedByUser: {
1210
1696
  type: Boolean,
1211
1697
  required: true,
1212
1698
  default: false,
1213
1699
  },
1214
- password: {
1215
- type: String,
1216
- trim: true,
1217
- minlength: 8,
1218
- maxlength: 128,
1219
- select: false,
1700
+ unfinished: {
1701
+ type: Boolean,
1702
+ default: false,
1220
1703
  },
1221
- avatar: {
1704
+ error: {
1705
+ type: Boolean,
1706
+ default: false,
1707
+ },
1708
+ finish_reason: {
1222
1709
  type: String,
1710
+ },
1711
+ feedback: {
1712
+ type: {
1713
+ rating: {
1714
+ type: String,
1715
+ enum: ['thumbsUp', 'thumbsDown'],
1716
+ required: true,
1717
+ },
1718
+ tag: {
1719
+ type: mongoose.Schema.Types.Mixed,
1720
+ required: false,
1721
+ },
1722
+ text: {
1723
+ type: String,
1724
+ required: false,
1725
+ },
1726
+ },
1727
+ default: undefined,
1223
1728
  required: false,
1224
1729
  },
1225
- provider: {
1226
- type: String,
1227
- required: true,
1228
- default: 'local',
1730
+ _meiliIndex: {
1731
+ type: Boolean,
1732
+ required: false,
1733
+ select: false,
1734
+ default: false,
1229
1735
  },
1230
- role: {
1231
- type: String,
1232
- default: SystemRoles.USER,
1736
+ files: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
1737
+ plugin: {
1738
+ type: {
1739
+ latest: {
1740
+ type: String,
1741
+ required: false,
1742
+ },
1743
+ inputs: {
1744
+ type: [mongoose.Schema.Types.Mixed],
1745
+ required: false,
1746
+ default: undefined,
1747
+ },
1748
+ outputs: {
1749
+ type: String,
1750
+ required: false,
1751
+ },
1752
+ },
1753
+ default: undefined,
1233
1754
  },
1234
- googleId: {
1235
- type: String,
1236
- unique: true,
1237
- sparse: true,
1755
+ plugins: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
1756
+ content: {
1757
+ type: [{ type: mongoose.Schema.Types.Mixed }],
1758
+ default: undefined,
1759
+ meiliIndex: true,
1238
1760
  },
1239
- facebookId: {
1761
+ thread_id: {
1240
1762
  type: String,
1241
- unique: true,
1242
- sparse: true,
1243
1763
  },
1244
- openidId: {
1764
+ /* frontend components */
1765
+ iconURL: {
1245
1766
  type: String,
1246
- unique: true,
1247
- sparse: true,
1248
1767
  },
1249
- samlId: {
1768
+ metadata: { type: mongoose.Schema.Types.Mixed },
1769
+ attachments: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
1770
+ /*
1771
+ attachments: {
1772
+ type: [
1773
+ {
1774
+ file_id: String,
1775
+ filename: String,
1776
+ filepath: String,
1777
+ expiresAt: Date,
1778
+ width: Number,
1779
+ height: Number,
1780
+ type: String,
1781
+ conversationId: String,
1782
+ messageId: {
1783
+ type: String,
1784
+ required: true,
1785
+ },
1786
+ toolCallId: String,
1787
+ },
1788
+ ],
1789
+ default: undefined,
1790
+ },
1791
+ */
1792
+ expiredAt: {
1793
+ type: Date,
1794
+ },
1795
+ }, { timestamps: true });
1796
+ messageSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
1797
+ messageSchema.index({ createdAt: 1 });
1798
+ messageSchema.index({ messageId: 1, user: 1 }, { unique: true });
1799
+
1800
+ const pluginAuthSchema = new Schema({
1801
+ authField: {
1250
1802
  type: String,
1251
- unique: true,
1252
- sparse: true,
1803
+ required: true,
1253
1804
  },
1254
- ldapId: {
1805
+ value: {
1255
1806
  type: String,
1256
- unique: true,
1257
- sparse: true,
1807
+ required: true,
1258
1808
  },
1259
- githubId: {
1809
+ userId: {
1260
1810
  type: String,
1261
- unique: true,
1262
- sparse: true,
1811
+ required: true,
1263
1812
  },
1264
- discordId: {
1813
+ pluginKey: {
1265
1814
  type: String,
1266
- unique: true,
1267
- sparse: true,
1268
1815
  },
1269
- appleId: {
1816
+ }, { timestamps: true });
1817
+
1818
+ const presetSchema = new Schema({
1819
+ presetId: {
1270
1820
  type: String,
1271
1821
  unique: true,
1272
- sparse: true,
1273
- },
1274
- plugins: {
1275
- type: Array,
1822
+ required: true,
1823
+ index: true,
1276
1824
  },
1277
- twoFactorEnabled: {
1278
- type: Boolean,
1279
- default: false,
1825
+ title: {
1826
+ type: String,
1827
+ default: 'New Chat',
1828
+ meiliIndex: true,
1280
1829
  },
1281
- totpSecret: {
1830
+ user: {
1282
1831
  type: String,
1283
- select: false,
1832
+ default: null,
1284
1833
  },
1285
- backupCodes: {
1286
- type: [BackupCodeSchema],
1287
- select: false,
1834
+ defaultPreset: {
1835
+ type: Boolean,
1288
1836
  },
1289
- refreshToken: {
1290
- type: [SessionSchema],
1837
+ order: {
1838
+ type: Number,
1291
1839
  },
1292
- expiresAt: {
1293
- type: Date,
1294
- expires: 604800, // 7 days in seconds
1840
+ ...conversationPreset,
1841
+ agentOptions: {
1842
+ type: mongoose.Schema.Types.Mixed,
1843
+ default: null,
1295
1844
  },
1296
- termsAccepted: {
1297
- type: Boolean,
1298
- default: false,
1845
+ }, { timestamps: true });
1846
+
1847
+ const projectSchema = new Schema({
1848
+ name: {
1849
+ type: String,
1850
+ required: true,
1851
+ index: true,
1299
1852
  },
1300
- personalization: {
1301
- type: {
1302
- memories: {
1303
- type: Boolean,
1304
- default: true,
1305
- },
1306
- },
1307
- default: {},
1853
+ promptGroupIds: {
1854
+ type: [Schema.Types.ObjectId],
1855
+ ref: 'PromptGroup',
1856
+ default: [],
1308
1857
  },
1309
- /** Field for external source identification (for consistency with TPrincipal schema) */
1310
- idOnTheSource: {
1311
- type: String,
1312
- sparse: true,
1858
+ agentIds: {
1859
+ type: [String],
1860
+ ref: 'Agent',
1861
+ default: [],
1313
1862
  },
1314
- }, { timestamps: true });
1863
+ }, {
1864
+ timestamps: true,
1865
+ });
1315
1866
 
1316
- const MemoryEntrySchema = new Schema({
1317
- userId: {
1867
+ const promptSchema = new Schema({
1868
+ groupId: {
1318
1869
  type: Schema.Types.ObjectId,
1319
- ref: 'User',
1870
+ ref: 'PromptGroup',
1871
+ required: true,
1320
1872
  index: true,
1873
+ },
1874
+ author: {
1875
+ type: Schema.Types.ObjectId,
1876
+ ref: 'User',
1321
1877
  required: true,
1322
1878
  },
1323
- key: {
1879
+ prompt: {
1324
1880
  type: String,
1325
1881
  required: true,
1326
- validate: {
1327
- validator: (v) => /^[a-z_]+$/.test(v),
1328
- message: 'Key must only contain lowercase letters and underscores',
1329
- },
1330
1882
  },
1331
- value: {
1883
+ type: {
1332
1884
  type: String,
1885
+ enum: ['text', 'chat'],
1333
1886
  required: true,
1334
1887
  },
1335
- tokenCount: {
1336
- type: Number,
1337
- default: 0,
1338
- },
1339
- updated_at: {
1340
- type: Date,
1341
- default: Date.now,
1342
- },
1888
+ }, {
1889
+ timestamps: true,
1343
1890
  });
1891
+ promptSchema.index({ createdAt: 1, updatedAt: 1 });
1344
1892
 
1345
- const groupSchema = new Schema({
1893
+ const promptGroupSchema = new Schema({
1346
1894
  name: {
1347
1895
  type: String,
1348
1896
  required: true,
1349
1897
  index: true,
1350
1898
  },
1351
- description: {
1899
+ numberOfGenerations: {
1900
+ type: Number,
1901
+ default: 0,
1902
+ },
1903
+ oneliner: {
1352
1904
  type: String,
1353
- required: false,
1905
+ default: '',
1354
1906
  },
1355
- email: {
1907
+ category: {
1356
1908
  type: String,
1357
- required: false,
1909
+ default: '',
1358
1910
  index: true,
1359
1911
  },
1360
- avatar: {
1361
- type: String,
1362
- required: false,
1912
+ projectIds: {
1913
+ type: [Schema.Types.ObjectId],
1914
+ ref: 'Project',
1915
+ index: true,
1916
+ default: [],
1363
1917
  },
1364
- memberIds: [
1365
- {
1366
- type: String,
1367
- required: false,
1368
- },
1369
- ],
1370
- source: {
1918
+ productionId: {
1919
+ type: Schema.Types.ObjectId,
1920
+ ref: 'Prompt',
1921
+ required: true,
1922
+ index: true,
1923
+ },
1924
+ author: {
1925
+ type: Schema.Types.ObjectId,
1926
+ ref: 'User',
1927
+ required: true,
1928
+ index: true,
1929
+ },
1930
+ authorName: {
1371
1931
  type: String,
1372
- enum: ['local', 'entra'],
1373
- default: 'local',
1932
+ required: true,
1374
1933
  },
1375
- /** External ID (e.g., Entra ID) */
1376
- idOnTheSource: {
1934
+ command: {
1377
1935
  type: String,
1378
- sparse: true,
1379
1936
  index: true,
1380
- required: function () {
1381
- return this.source !== 'local';
1382
- },
1383
- },
1384
- }, { timestamps: true });
1385
- groupSchema.index({ idOnTheSource: 1, source: 1 }, {
1386
- unique: true,
1387
- partialFilterExpression: { idOnTheSource: { $exists: true } },
1388
- });
1389
- groupSchema.index({ memberIds: 1 });
1390
-
1391
- /**
1392
- * ESM-native object traversal utility
1393
- * Simplified implementation focused on the forEach use case
1394
- */
1395
- function isObject(value) {
1396
- if (value === null || typeof value !== 'object') {
1397
- return false;
1398
- }
1399
- // Treat these built-in types as leaf nodes, not objects to traverse
1400
- if (value instanceof Date)
1401
- return false;
1402
- if (value instanceof RegExp)
1403
- return false;
1404
- if (value instanceof Error)
1405
- return false;
1406
- if (value instanceof URL)
1407
- return false;
1408
- // Check for Buffer (Node.js)
1409
- if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value))
1410
- return false;
1411
- // Check for TypedArrays and ArrayBuffer
1412
- if (ArrayBuffer.isView(value))
1413
- return false;
1414
- if (value instanceof ArrayBuffer)
1415
- return false;
1416
- if (value instanceof SharedArrayBuffer)
1417
- return false;
1418
- // Check for other built-in types that shouldn't be traversed
1419
- if (value instanceof Promise)
1420
- return false;
1421
- if (value instanceof WeakMap)
1422
- return false;
1423
- if (value instanceof WeakSet)
1424
- return false;
1425
- if (value instanceof Map)
1426
- return false;
1427
- if (value instanceof Set)
1428
- return false;
1429
- // Check if it's a primitive wrapper object
1430
- const stringTag = Object.prototype.toString.call(value);
1431
- if (stringTag === '[object Boolean]' ||
1432
- stringTag === '[object Number]' ||
1433
- stringTag === '[object String]') {
1434
- return false;
1435
- }
1436
- return true;
1437
- }
1438
- // Helper to safely set a property on an object or array
1439
- function setProperty(obj, key, value) {
1440
- if (Array.isArray(obj) && typeof key === 'number') {
1441
- obj[key] = value;
1442
- }
1443
- else if (!Array.isArray(obj) && typeof key === 'string') {
1444
- obj[key] = value;
1445
- }
1446
- else if (!Array.isArray(obj) && typeof key === 'number') {
1447
- // Handle numeric keys on objects
1448
- obj[key] = value;
1449
- }
1450
- }
1451
- // Helper to safely delete a property from an object
1452
- function deleteProperty(obj, key) {
1453
- if (Array.isArray(obj) && typeof key === 'number') {
1454
- // For arrays, we should use splice, but this is handled in remove()
1455
- // This function is only called for non-array deletion
1456
- return;
1457
- }
1458
- if (!Array.isArray(obj)) {
1459
- delete obj[key];
1460
- }
1461
- }
1462
- function forEach(obj, callback) {
1463
- const visited = new WeakSet();
1464
- function walk(node, path = [], parent) {
1465
- // Check for circular references
1466
- let circular = null;
1467
- if (isObject(node)) {
1468
- if (visited.has(node)) {
1469
- // Find the circular reference in the parent chain
1470
- let p = parent;
1471
- while (p) {
1472
- if (p.node === node) {
1473
- circular = p;
1474
- break;
1475
- }
1476
- p = p.parent;
1477
- }
1478
- return; // Skip circular references
1479
- }
1480
- visited.add(node);
1481
- }
1482
- const key = path.length > 0 ? path[path.length - 1] : undefined;
1483
- const isRoot = path.length === 0;
1484
- const level = path.length;
1485
- // Determine if this is a leaf node
1486
- const isLeaf = !isObject(node) ||
1487
- (Array.isArray(node) && node.length === 0) ||
1488
- Object.keys(node).length === 0;
1489
- // Create context
1490
- const context = {
1491
- node,
1492
- path: [...path],
1493
- parent,
1494
- key,
1495
- isLeaf,
1496
- notLeaf: !isLeaf,
1497
- isRoot,
1498
- notRoot: !isRoot,
1499
- level,
1500
- circular,
1501
- update(value) {
1502
- if (!isRoot && parent && key !== undefined && isObject(parent.node)) {
1503
- setProperty(parent.node, key, value);
1504
- }
1505
- this.node = value;
1506
- },
1507
- remove() {
1508
- if (!isRoot && parent && key !== undefined && isObject(parent.node)) {
1509
- if (Array.isArray(parent.node) && typeof key === 'number') {
1510
- parent.node.splice(key, 1);
1511
- }
1512
- else {
1513
- deleteProperty(parent.node, key);
1514
- }
1515
- }
1937
+ validate: {
1938
+ validator: function (v) {
1939
+ return v === undefined || v === null || v === '' || /^[a-z0-9-]+$/.test(v);
1516
1940
  },
1517
- };
1518
- // Call the callback with the context
1519
- callback.call(context, node);
1520
- // Traverse children if not circular and is an object
1521
- if (!circular && isObject(node) && !isLeaf) {
1522
- if (Array.isArray(node)) {
1523
- for (let i = 0; i < node.length; i++) {
1524
- walk(node[i], [...path, i], context);
1525
- }
1526
- }
1527
- else {
1528
- for (const [childKey, childValue] of Object.entries(node)) {
1529
- walk(childValue, [...path, childKey], context);
1530
- }
1531
- }
1532
- }
1533
- }
1534
- walk(obj);
1535
- }
1536
- // Main traverse function that returns an object with forEach method
1537
- function traverse(obj) {
1538
- return {
1539
- forEach(callback) {
1540
- forEach(obj, callback);
1941
+ message: (props) => { var _a; return `${(_a = props === null || props === void 0 ? void 0 : props.value) !== null && _a !== void 0 ? _a : 'Value'} is not a valid command. Only lowercase alphanumeric characters and hyphens are allowed.`; },
1541
1942
  },
1542
- };
1543
- }
1943
+ maxlength: [
1944
+ Constants.COMMANDS_MAX_LENGTH,
1945
+ `Command cannot be longer than ${Constants.COMMANDS_MAX_LENGTH} characters`,
1946
+ ],
1947
+ }, // Casting here bypasses the type error for the command field.
1948
+ }, {
1949
+ timestamps: true,
1950
+ });
1951
+ promptGroupSchema.index({ createdAt: 1, updatedAt: 1 });
1544
1952
 
1545
- const SPLAT_SYMBOL = Symbol.for('splat');
1546
- const MESSAGE_SYMBOL = Symbol.for('message');
1547
- const CONSOLE_JSON_STRING_LENGTH = parseInt(process.env.CONSOLE_JSON_STRING_LENGTH || '', 10) || 255;
1548
- const sensitiveKeys = [
1549
- /^(sk-)[^\s]+/, // OpenAI API key pattern
1550
- /(Bearer )[^\s]+/, // Header: Bearer token pattern
1551
- /(api-key:? )[^\s]+/, // Header: API key pattern
1552
- /(key=)[^\s]+/, // URL query param: sensitive key pattern (Google)
1553
- ];
1554
- /**
1555
- * Determines if a given value string is sensitive and returns matching regex patterns.
1556
- *
1557
- * @param valueStr - The value string to check.
1558
- * @returns An array of regex patterns that match the value string.
1559
- */
1560
- function getMatchingSensitivePatterns(valueStr) {
1561
- if (valueStr) {
1562
- // Filter and return all regex patterns that match the value string
1563
- return sensitiveKeys.filter((regex) => regex.test(valueStr));
1564
- }
1565
- return [];
1566
- }
1567
- /**
1568
- * Redacts sensitive information from a console message and trims it to a specified length if provided.
1569
- * @param str - The console message to be redacted.
1570
- * @param trimLength - The optional length at which to trim the redacted message.
1571
- * @returns The redacted and optionally trimmed console message.
1572
- */
1573
- function redactMessage(str, trimLength) {
1574
- if (!str) {
1575
- return '';
1576
- }
1577
- const patterns = getMatchingSensitivePatterns(str);
1578
- patterns.forEach((pattern) => {
1579
- str = str.replace(pattern, '$1[REDACTED]');
1580
- });
1581
- return str;
1582
- }
1583
1953
  /**
1584
- * Redacts sensitive information from log messages if the log level is 'error'.
1585
- * Note: Intentionally mutates the object.
1586
- * @param info - The log information object.
1587
- * @returns The modified log information object.
1954
+ * Uses a sub-schema for permissions. Notice we disable `_id` for this subdocument.
1588
1955
  */
1589
- const redactFormat = winston.format((info) => {
1590
- if (info.level === 'error') {
1591
- // Type guard to ensure message is a string
1592
- if (typeof info.message === 'string') {
1593
- info.message = redactMessage(info.message);
1594
- }
1595
- // Handle MESSAGE_SYMBOL with type safety
1596
- const symbolValue = info[MESSAGE_SYMBOL];
1597
- if (typeof symbolValue === 'string') {
1598
- info[MESSAGE_SYMBOL] = redactMessage(symbolValue);
1599
- }
1600
- }
1601
- return info;
1956
+ const rolePermissionsSchema = new Schema({
1957
+ [PermissionTypes.BOOKMARKS]: {
1958
+ [Permissions.USE]: { type: Boolean },
1959
+ },
1960
+ [PermissionTypes.PROMPTS]: {
1961
+ [Permissions.SHARED_GLOBAL]: { type: Boolean },
1962
+ [Permissions.USE]: { type: Boolean },
1963
+ [Permissions.CREATE]: { type: Boolean },
1964
+ },
1965
+ [PermissionTypes.MEMORIES]: {
1966
+ [Permissions.USE]: { type: Boolean },
1967
+ [Permissions.CREATE]: { type: Boolean },
1968
+ [Permissions.UPDATE]: { type: Boolean },
1969
+ [Permissions.READ]: { type: Boolean },
1970
+ [Permissions.OPT_OUT]: { type: Boolean },
1971
+ },
1972
+ [PermissionTypes.AGENTS]: {
1973
+ [Permissions.SHARED_GLOBAL]: { type: Boolean },
1974
+ [Permissions.USE]: { type: Boolean },
1975
+ [Permissions.CREATE]: { type: Boolean },
1976
+ },
1977
+ [PermissionTypes.MULTI_CONVO]: {
1978
+ [Permissions.USE]: { type: Boolean },
1979
+ },
1980
+ [PermissionTypes.TEMPORARY_CHAT]: {
1981
+ [Permissions.USE]: { type: Boolean },
1982
+ },
1983
+ [PermissionTypes.RUN_CODE]: {
1984
+ [Permissions.USE]: { type: Boolean },
1985
+ },
1986
+ [PermissionTypes.WEB_SEARCH]: {
1987
+ [Permissions.USE]: { type: Boolean },
1988
+ },
1989
+ [PermissionTypes.PEOPLE_PICKER]: {
1990
+ [Permissions.VIEW_USERS]: { type: Boolean },
1991
+ [Permissions.VIEW_GROUPS]: { type: Boolean },
1992
+ [Permissions.VIEW_ROLES]: { type: Boolean },
1993
+ },
1994
+ [PermissionTypes.MARKETPLACE]: {
1995
+ [Permissions.USE]: { type: Boolean },
1996
+ },
1997
+ [PermissionTypes.FILE_SEARCH]: {
1998
+ [Permissions.USE]: { type: Boolean },
1999
+ },
2000
+ [PermissionTypes.FILE_CITATIONS]: {
2001
+ [Permissions.USE]: { type: Boolean },
2002
+ },
2003
+ }, { _id: false });
2004
+ const roleSchema = new Schema({
2005
+ name: { type: String, required: true, unique: true, index: true },
2006
+ permissions: {
2007
+ type: rolePermissionsSchema,
2008
+ },
1602
2009
  });
1603
- /**
1604
- * Truncates long strings, especially base64 image data, within log messages.
1605
- *
1606
- * @param value - The value to be inspected and potentially truncated.
1607
- * @param length - The length at which to truncate the value. Default: 100.
1608
- * @returns The truncated or original value.
1609
- */
1610
- const truncateLongStrings = (value, length = 100) => {
1611
- if (typeof value === 'string') {
1612
- return value.length > length ? value.substring(0, length) + '... [truncated]' : value;
1613
- }
1614
- return value;
1615
- };
1616
- /**
1617
- * An array mapping function that truncates long strings (objects converted to JSON strings).
1618
- * @param item - The item to be condensed.
1619
- * @returns The condensed item.
1620
- */
1621
- const condenseArray = (item) => {
1622
- if (typeof item === 'string') {
1623
- return truncateLongStrings(JSON.stringify(item));
1624
- }
1625
- else if (typeof item === 'object') {
1626
- return truncateLongStrings(JSON.stringify(item));
1627
- }
1628
- return item;
1629
- };
1630
- /**
1631
- * Formats log messages for debugging purposes.
1632
- * - Truncates long strings within log messages.
1633
- * - Condenses arrays by truncating long strings and objects as strings within array items.
1634
- * - Redacts sensitive information from log messages if the log level is 'error'.
1635
- * - Converts log information object to a formatted string.
1636
- *
1637
- * @param options - The options for formatting log messages.
1638
- * @returns The formatted log message.
1639
- */
1640
- const debugTraverse = winston.format.printf(({ level, message, timestamp, ...metadata }) => {
1641
- if (!message) {
1642
- return `${timestamp} ${level}`;
1643
- }
1644
- // Type-safe version of the CJS logic: !message?.trim || typeof message !== 'string'
1645
- if (typeof message !== 'string' || !message.trim) {
1646
- return `${timestamp} ${level}: ${JSON.stringify(message)}`;
1647
- }
1648
- const msgParts = [
1649
- `${timestamp} ${level}: ${truncateLongStrings(message.trim(), 150)}`,
1650
- ];
1651
- try {
1652
- if (level !== 'debug') {
1653
- return msgParts[0];
1654
- }
1655
- if (!metadata) {
1656
- return msgParts[0];
1657
- }
1658
- // Type-safe access to SPLAT_SYMBOL using bracket notation
1659
- const metadataRecord = metadata;
1660
- const splatArray = metadataRecord[SPLAT_SYMBOL];
1661
- const debugValue = Array.isArray(splatArray) ? splatArray[0] : undefined;
1662
- if (!debugValue) {
1663
- return msgParts[0];
1664
- }
1665
- if (debugValue && Array.isArray(debugValue)) {
1666
- msgParts.push(`\n${JSON.stringify(debugValue.map(condenseArray))}`);
1667
- return msgParts.join('');
1668
- }
1669
- if (typeof debugValue !== 'object') {
1670
- msgParts.push(` ${debugValue}`);
1671
- return msgParts.join('');
1672
- }
1673
- msgParts.push('\n{');
1674
- const copy = klona(metadata);
1675
- try {
1676
- const traversal = traverse(copy);
1677
- traversal.forEach(function (value) {
1678
- var _a;
1679
- if (typeof (this === null || this === void 0 ? void 0 : this.key) === 'symbol') {
1680
- return;
1681
- }
1682
- let _parentKey = '';
1683
- const parent = this.parent;
1684
- if (typeof (parent === null || parent === void 0 ? void 0 : parent.key) !== 'symbol' && (parent === null || parent === void 0 ? void 0 : parent.key) !== undefined) {
1685
- _parentKey = String(parent.key);
1686
- }
1687
- const parentKey = `${parent && parent.notRoot ? _parentKey + '.' : ''}`;
1688
- const tabs = `${parent && parent.notRoot ? ' ' : ' '}`;
1689
- const currentKey = (_a = this === null || this === void 0 ? void 0 : this.key) !== null && _a !== void 0 ? _a : 'unknown';
1690
- if (this.isLeaf && typeof value === 'string') {
1691
- const truncatedText = truncateLongStrings(value);
1692
- msgParts.push(`\n${tabs}${parentKey}${currentKey}: ${JSON.stringify(truncatedText)},`);
1693
- }
1694
- else if (this.notLeaf && Array.isArray(value) && value.length > 0) {
1695
- const currentMessage = `\n${tabs}// ${value.length} ${String(currentKey).replace(/s$/, '')}(s)`;
1696
- this.update(currentMessage);
1697
- msgParts.push(currentMessage);
1698
- const stringifiedArray = value.map(condenseArray);
1699
- msgParts.push(`\n${tabs}${parentKey}${currentKey}: [${stringifiedArray}],`);
1700
- }
1701
- else if (this.isLeaf && typeof value === 'function') {
1702
- msgParts.push(`\n${tabs}${parentKey}${currentKey}: function,`);
1703
- }
1704
- else if (this.isLeaf) {
1705
- msgParts.push(`\n${tabs}${parentKey}${currentKey}: ${value},`);
1706
- }
1707
- });
1708
- }
1709
- catch (e) {
1710
- const errorMessage = e instanceof Error ? e.message : 'Unknown error';
1711
- msgParts.push(`\n[LOGGER TRAVERSAL ERROR] ${errorMessage}`);
1712
- }
1713
- msgParts.push('\n}');
1714
- return msgParts.join('');
1715
- }
1716
- catch (e) {
1717
- const errorMessage = e instanceof Error ? e.message : 'Unknown error';
1718
- msgParts.push(`\n[LOGGER PARSING ERROR] ${errorMessage}`);
1719
- return msgParts.join('');
1720
- }
2010
+
2011
+ const sessionSchema = new Schema({
2012
+ refreshTokenHash: {
2013
+ type: String,
2014
+ required: true,
2015
+ },
2016
+ expiration: {
2017
+ type: Date,
2018
+ required: true,
2019
+ expires: 0,
2020
+ },
2021
+ user: {
2022
+ type: mongoose.Schema.Types.ObjectId,
2023
+ ref: 'User',
2024
+ required: true,
2025
+ },
2026
+ });
2027
+
2028
+ const shareSchema = new Schema({
2029
+ conversationId: {
2030
+ type: String,
2031
+ required: true,
2032
+ },
2033
+ title: {
2034
+ type: String,
2035
+ index: true,
2036
+ },
2037
+ user: {
2038
+ type: String,
2039
+ index: true,
2040
+ },
2041
+ messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
2042
+ shareId: {
2043
+ type: String,
2044
+ index: true,
2045
+ },
2046
+ targetMessageId: {
2047
+ type: String,
2048
+ required: false,
2049
+ index: true,
2050
+ },
2051
+ isPublic: {
2052
+ type: Boolean,
2053
+ default: true,
2054
+ },
2055
+ }, { timestamps: true });
2056
+ shareSchema.index({ conversationId: 1, user: 1, targetMessageId: 1 });
2057
+
2058
+ const tokenSchema = new Schema({
2059
+ userId: {
2060
+ type: Schema.Types.ObjectId,
2061
+ required: true,
2062
+ ref: 'user',
2063
+ },
2064
+ email: {
2065
+ type: String,
2066
+ },
2067
+ type: {
2068
+ type: String,
2069
+ },
2070
+ identifier: {
2071
+ type: String,
2072
+ },
2073
+ token: {
2074
+ type: String,
2075
+ required: true,
2076
+ },
2077
+ createdAt: {
2078
+ type: Date,
2079
+ required: true,
2080
+ default: Date.now,
2081
+ },
2082
+ expiresAt: {
2083
+ type: Date,
2084
+ required: true,
2085
+ },
2086
+ metadata: {
2087
+ type: Map,
2088
+ of: Schema.Types.Mixed,
2089
+ },
1721
2090
  });
1722
- /**
1723
- * Truncates long string values in JSON log objects.
1724
- * Prevents outputting extremely long values (e.g., base64, blobs).
1725
- */
1726
- const jsonTruncateFormat = winston.format((info) => {
1727
- const truncateLongStrings = (str, maxLength) => str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
1728
- const seen = new WeakSet();
1729
- const truncateObject = (obj) => {
1730
- if (typeof obj !== 'object' || obj === null) {
1731
- return obj;
1732
- }
1733
- // Handle circular references - now with proper object type
1734
- if (seen.has(obj)) {
1735
- return '[Circular]';
1736
- }
1737
- seen.add(obj);
1738
- if (Array.isArray(obj)) {
1739
- return obj.map((item) => truncateObject(item));
1740
- }
1741
- // We know this is an object at this point
1742
- const objectRecord = obj;
1743
- const newObj = {};
1744
- Object.entries(objectRecord).forEach(([key, value]) => {
1745
- if (typeof value === 'string') {
1746
- newObj[key] = truncateLongStrings(value, CONSOLE_JSON_STRING_LENGTH);
1747
- }
1748
- else {
1749
- newObj[key] = truncateObject(value);
1750
- }
1751
- });
1752
- return newObj;
1753
- };
1754
- return truncateObject(info);
2091
+ tokenSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
2092
+
2093
+ const toolCallSchema = new Schema({
2094
+ conversationId: {
2095
+ type: String,
2096
+ required: true,
2097
+ },
2098
+ messageId: {
2099
+ type: String,
2100
+ required: true,
2101
+ },
2102
+ toolId: {
2103
+ type: String,
2104
+ required: true,
2105
+ },
2106
+ user: {
2107
+ type: mongoose.Schema.Types.ObjectId,
2108
+ ref: 'User',
2109
+ required: true,
2110
+ },
2111
+ result: {
2112
+ type: mongoose.Schema.Types.Mixed,
2113
+ },
2114
+ attachments: {
2115
+ type: mongoose.Schema.Types.Mixed,
2116
+ },
2117
+ blockIndex: {
2118
+ type: Number,
2119
+ },
2120
+ partIndex: {
2121
+ type: Number,
2122
+ },
2123
+ }, { timestamps: true });
2124
+ toolCallSchema.index({ messageId: 1, user: 1 });
2125
+ toolCallSchema.index({ conversationId: 1, user: 1 });
2126
+
2127
+ const transactionSchema = new Schema({
2128
+ user: {
2129
+ type: mongoose.Schema.Types.ObjectId,
2130
+ ref: 'User',
2131
+ index: true,
2132
+ required: true,
2133
+ },
2134
+ conversationId: {
2135
+ type: String,
2136
+ ref: 'Conversation',
2137
+ index: true,
2138
+ },
2139
+ tokenType: {
2140
+ type: String,
2141
+ enum: ['prompt', 'completion', 'credits'],
2142
+ required: true,
2143
+ },
2144
+ model: {
2145
+ type: String,
2146
+ },
2147
+ context: {
2148
+ type: String,
2149
+ },
2150
+ valueKey: {
2151
+ type: String,
2152
+ },
2153
+ rate: Number,
2154
+ rawAmount: Number,
2155
+ tokenValue: Number,
2156
+ inputTokens: { type: Number },
2157
+ writeTokens: { type: Number },
2158
+ readTokens: { type: Number },
2159
+ }, {
2160
+ timestamps: true,
1755
2161
  });
1756
2162
 
1757
- /**
1758
- * Determine the log directory in a cross-compatible way.
1759
- * Priority:
1760
- * 1. LIBRECHAT_LOG_DIR environment variable
1761
- * 2. If running within LibreChat monorepo (when cwd ends with /api), use api/logs
1762
- * 3. If api/logs exists relative to cwd, use that (for running from project root)
1763
- * 4. Otherwise, use logs directory relative to process.cwd()
1764
- *
1765
- * This avoids using __dirname which is not available in ESM modules
1766
- */
1767
- const getLogDirectory = () => {
1768
- if (process.env.LIBRECHAT_LOG_DIR) {
1769
- return process.env.LIBRECHAT_LOG_DIR;
1770
- }
1771
- const cwd = process.cwd();
1772
- // Check if we're running from within the api directory
1773
- if (cwd.endsWith('/api') || cwd.endsWith('\\api')) {
1774
- return path.join(cwd, 'logs');
1775
- }
1776
- // Check if api/logs exists relative to current directory (running from project root)
1777
- // We'll just use the path and let the file system create it if needed
1778
- const apiLogsPath = path.join(cwd, 'api', 'logs');
1779
- // For LibreChat project structure, use api/logs
1780
- // For external consumers, they should set LIBRECHAT_LOG_DIR
1781
- if (cwd.includes('LibreChat')) {
1782
- return apiLogsPath;
1783
- }
1784
- // Default to logs directory relative to current working directory
1785
- return path.join(cwd, 'logs');
1786
- };
2163
+ // Session sub-schema
2164
+ const SessionSchema = new Schema({
2165
+ refreshToken: {
2166
+ type: String,
2167
+ default: '',
2168
+ },
2169
+ }, { _id: false });
2170
+ // Backup code sub-schema
2171
+ const BackupCodeSchema = new Schema({
2172
+ codeHash: { type: String, required: true },
2173
+ used: { type: Boolean, default: false },
2174
+ usedAt: { type: Date, default: null },
2175
+ }, { _id: false });
2176
+ const userSchema = new Schema({
2177
+ name: {
2178
+ type: String,
2179
+ },
2180
+ username: {
2181
+ type: String,
2182
+ lowercase: true,
2183
+ default: '',
2184
+ },
2185
+ email: {
2186
+ type: String,
2187
+ required: [true, "can't be blank"],
2188
+ lowercase: true,
2189
+ unique: true,
2190
+ match: [/\S+@\S+\.\S+/, 'is invalid'],
2191
+ index: true,
2192
+ },
2193
+ emailVerified: {
2194
+ type: Boolean,
2195
+ required: true,
2196
+ default: false,
2197
+ },
2198
+ password: {
2199
+ type: String,
2200
+ trim: true,
2201
+ minlength: 8,
2202
+ maxlength: 128,
2203
+ select: false,
2204
+ },
2205
+ avatar: {
2206
+ type: String,
2207
+ required: false,
2208
+ },
2209
+ provider: {
2210
+ type: String,
2211
+ required: true,
2212
+ default: 'local',
2213
+ },
2214
+ role: {
2215
+ type: String,
2216
+ default: SystemRoles.USER,
2217
+ },
2218
+ googleId: {
2219
+ type: String,
2220
+ unique: true,
2221
+ sparse: true,
2222
+ },
2223
+ facebookId: {
2224
+ type: String,
2225
+ unique: true,
2226
+ sparse: true,
2227
+ },
2228
+ openidId: {
2229
+ type: String,
2230
+ unique: true,
2231
+ sparse: true,
2232
+ },
2233
+ samlId: {
2234
+ type: String,
2235
+ unique: true,
2236
+ sparse: true,
2237
+ },
2238
+ ldapId: {
2239
+ type: String,
2240
+ unique: true,
2241
+ sparse: true,
2242
+ },
2243
+ githubId: {
2244
+ type: String,
2245
+ unique: true,
2246
+ sparse: true,
2247
+ },
2248
+ discordId: {
2249
+ type: String,
2250
+ unique: true,
2251
+ sparse: true,
2252
+ },
2253
+ appleId: {
2254
+ type: String,
2255
+ unique: true,
2256
+ sparse: true,
2257
+ },
2258
+ plugins: {
2259
+ type: Array,
2260
+ },
2261
+ twoFactorEnabled: {
2262
+ type: Boolean,
2263
+ default: false,
2264
+ },
2265
+ totpSecret: {
2266
+ type: String,
2267
+ select: false,
2268
+ },
2269
+ backupCodes: {
2270
+ type: [BackupCodeSchema],
2271
+ select: false,
2272
+ },
2273
+ refreshToken: {
2274
+ type: [SessionSchema],
2275
+ },
2276
+ expiresAt: {
2277
+ type: Date,
2278
+ expires: 604800, // 7 days in seconds
2279
+ },
2280
+ termsAccepted: {
2281
+ type: Boolean,
2282
+ default: false,
2283
+ },
2284
+ personalization: {
2285
+ type: {
2286
+ memories: {
2287
+ type: Boolean,
2288
+ default: true,
2289
+ },
2290
+ },
2291
+ default: {},
2292
+ },
2293
+ /** Field for external source identification (for consistency with TPrincipal schema) */
2294
+ idOnTheSource: {
2295
+ type: String,
2296
+ sparse: true,
2297
+ },
2298
+ }, { timestamps: true });
1787
2299
 
1788
- const logDir$1 = getLogDirectory();
1789
- const { NODE_ENV: NODE_ENV$1, DEBUG_LOGGING: DEBUG_LOGGING$1, CONSOLE_JSON, DEBUG_CONSOLE } = process.env;
1790
- const useConsoleJson = typeof CONSOLE_JSON === 'string' && CONSOLE_JSON.toLowerCase() === 'true';
1791
- const useDebugConsole = typeof DEBUG_CONSOLE === 'string' && DEBUG_CONSOLE.toLowerCase() === 'true';
1792
- const useDebugLogging$1 = typeof DEBUG_LOGGING$1 === 'string' && DEBUG_LOGGING$1.toLowerCase() === 'true';
1793
- const levels$1 = {
1794
- error: 0,
1795
- warn: 1,
1796
- info: 2,
1797
- http: 3,
1798
- verbose: 4,
1799
- debug: 5,
1800
- activity: 6,
1801
- silly: 7,
1802
- };
1803
- winston.addColors({
1804
- info: 'green',
1805
- warn: 'italic yellow',
1806
- error: 'red',
1807
- debug: 'blue',
2300
+ const MemoryEntrySchema = new Schema({
2301
+ userId: {
2302
+ type: Schema.Types.ObjectId,
2303
+ ref: 'User',
2304
+ index: true,
2305
+ required: true,
2306
+ },
2307
+ key: {
2308
+ type: String,
2309
+ required: true,
2310
+ validate: {
2311
+ validator: (v) => /^[a-z_]+$/.test(v),
2312
+ message: 'Key must only contain lowercase letters and underscores',
2313
+ },
2314
+ },
2315
+ value: {
2316
+ type: String,
2317
+ required: true,
2318
+ },
2319
+ tokenCount: {
2320
+ type: Number,
2321
+ default: 0,
2322
+ },
2323
+ updated_at: {
2324
+ type: Date,
2325
+ default: Date.now,
2326
+ },
1808
2327
  });
1809
- const level$1 = () => {
1810
- const env = NODE_ENV$1 || 'development';
1811
- return env === 'development' ? 'debug' : 'warn';
1812
- };
1813
- const fileFormat$1 = winston.format.combine(redactFormat(), winston.format.timestamp({ format: () => new Date().toISOString() }), winston.format.errors({ stack: true }), winston.format.splat());
1814
- const transports$1 = [
1815
- new winston.transports.DailyRotateFile({
1816
- level: 'error',
1817
- filename: `${logDir$1}/error-%DATE%.log`,
1818
- datePattern: 'YYYY-MM-DD',
1819
- zippedArchive: true,
1820
- maxSize: '20m',
1821
- maxFiles: '14d',
1822
- format: winston.format.combine(fileFormat$1, winston.format.json()),
1823
- }),
1824
- ];
1825
- if (useDebugLogging$1) {
1826
- transports$1.push(new winston.transports.DailyRotateFile({
1827
- level: 'debug',
1828
- filename: `${logDir$1}/debug-%DATE%.log`,
1829
- datePattern: 'YYYY-MM-DD',
1830
- zippedArchive: true,
1831
- maxSize: '20m',
1832
- maxFiles: '14d',
1833
- format: winston.format.combine(fileFormat$1, debugTraverse),
1834
- }));
1835
- }
1836
- const consoleFormat$1 = winston.format.combine(redactFormat(), winston.format.colorize({ all: true }), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf((info) => {
1837
- const message = `${info.timestamp} ${info.level}: ${info.message}`;
1838
- return info.level.includes('error') ? redactMessage(message) : message;
1839
- }));
1840
- let consoleLogLevel = 'info';
1841
- if (useDebugConsole) {
1842
- consoleLogLevel = 'debug';
1843
- }
1844
- // Add console transport
1845
- if (useDebugConsole) {
1846
- transports$1.push(new winston.transports.Console({
1847
- level: consoleLogLevel,
1848
- format: useConsoleJson
1849
- ? winston.format.combine(fileFormat$1, jsonTruncateFormat(), winston.format.json())
1850
- : winston.format.combine(fileFormat$1, debugTraverse),
1851
- }));
1852
- }
1853
- else if (useConsoleJson) {
1854
- transports$1.push(new winston.transports.Console({
1855
- level: consoleLogLevel,
1856
- format: winston.format.combine(fileFormat$1, jsonTruncateFormat(), winston.format.json()),
1857
- }));
1858
- }
1859
- else {
1860
- transports$1.push(new winston.transports.Console({
1861
- level: consoleLogLevel,
1862
- format: consoleFormat$1,
1863
- }));
1864
- }
1865
- // Create logger
1866
- const logger$1 = winston.createLogger({
1867
- level: level$1(),
1868
- levels: levels$1,
1869
- transports: transports$1,
2328
+
2329
+ const groupSchema = new Schema({
2330
+ name: {
2331
+ type: String,
2332
+ required: true,
2333
+ index: true,
2334
+ },
2335
+ description: {
2336
+ type: String,
2337
+ required: false,
2338
+ },
2339
+ email: {
2340
+ type: String,
2341
+ required: false,
2342
+ index: true,
2343
+ },
2344
+ avatar: {
2345
+ type: String,
2346
+ required: false,
2347
+ },
2348
+ memberIds: [
2349
+ {
2350
+ type: String,
2351
+ required: false,
2352
+ },
2353
+ ],
2354
+ source: {
2355
+ type: String,
2356
+ enum: ['local', 'entra'],
2357
+ default: 'local',
2358
+ },
2359
+ /** External ID (e.g., Entra ID) */
2360
+ idOnTheSource: {
2361
+ type: String,
2362
+ sparse: true,
2363
+ index: true,
2364
+ required: function () {
2365
+ return this.source !== 'local';
2366
+ },
2367
+ },
2368
+ }, { timestamps: true });
2369
+ groupSchema.index({ idOnTheSource: 1, source: 1 }, {
2370
+ unique: true,
2371
+ partialFilterExpression: { idOnTheSource: { $exists: true } },
1870
2372
  });
2373
+ groupSchema.index({ memberIds: 1 });
1871
2374
 
1872
2375
  /**
1873
2376
  * Checks if the connected MongoDB deployment supports transactions
@@ -4764,6 +5267,68 @@ function anonymizeMessages(messages, newConvoId) {
4764
5267
  };
4765
5268
  });
4766
5269
  }
5270
+ /**
5271
+ * Filter messages up to and including the target message (branch-specific)
5272
+ * Similar to getMessagesUpToTargetLevel from fork utilities
5273
+ */
5274
+ function getMessagesUpToTarget(messages, targetMessageId) {
5275
+ var _a, _b;
5276
+ if (!messages || messages.length === 0) {
5277
+ return [];
5278
+ }
5279
+ // If only one message and it's the target, return it
5280
+ if (messages.length === 1 && ((_a = messages[0]) === null || _a === void 0 ? void 0 : _a.messageId) === targetMessageId) {
5281
+ return messages;
5282
+ }
5283
+ // Create a map of parentMessageId to children messages
5284
+ const parentToChildrenMap = new Map();
5285
+ for (const message of messages) {
5286
+ const parentId = message.parentMessageId || Constants.NO_PARENT;
5287
+ if (!parentToChildrenMap.has(parentId)) {
5288
+ parentToChildrenMap.set(parentId, []);
5289
+ }
5290
+ (_b = parentToChildrenMap.get(parentId)) === null || _b === void 0 ? void 0 : _b.push(message);
5291
+ }
5292
+ // Find the target message
5293
+ const targetMessage = messages.find((msg) => msg.messageId === targetMessageId);
5294
+ if (!targetMessage) {
5295
+ // If target not found, return all messages for backwards compatibility
5296
+ return messages;
5297
+ }
5298
+ const visited = new Set();
5299
+ const rootMessages = parentToChildrenMap.get(Constants.NO_PARENT) || [];
5300
+ let currentLevel = rootMessages.length > 0 ? [...rootMessages] : [targetMessage];
5301
+ const results = new Set(currentLevel);
5302
+ // Check if the target message is at the root level
5303
+ if (currentLevel.some((msg) => msg.messageId === targetMessageId) &&
5304
+ targetMessage.parentMessageId === Constants.NO_PARENT) {
5305
+ return Array.from(results);
5306
+ }
5307
+ // Iterate level by level until the target is found
5308
+ let targetFound = false;
5309
+ while (!targetFound && currentLevel.length > 0) {
5310
+ const nextLevel = [];
5311
+ for (const node of currentLevel) {
5312
+ if (visited.has(node.messageId)) {
5313
+ continue;
5314
+ }
5315
+ visited.add(node.messageId);
5316
+ const children = parentToChildrenMap.get(node.messageId) || [];
5317
+ for (const child of children) {
5318
+ if (visited.has(child.messageId)) {
5319
+ continue;
5320
+ }
5321
+ nextLevel.push(child);
5322
+ results.add(child);
5323
+ if (child.messageId === targetMessageId) {
5324
+ targetFound = true;
5325
+ }
5326
+ }
5327
+ }
5328
+ currentLevel = nextLevel;
5329
+ }
5330
+ return Array.from(results);
5331
+ }
4767
5332
  /** Factory function that takes mongoose instance and returns the methods */
4768
5333
  function createShareMethods(mongoose) {
4769
5334
  /**
@@ -4782,6 +5347,11 @@ function createShareMethods(mongoose) {
4782
5347
  if (!(share === null || share === void 0 ? void 0 : share.conversationId) || !share.isPublic) {
4783
5348
  return null;
4784
5349
  }
5350
+ /** Filtered messages based on targetMessageId if present (branch-specific sharing) */
5351
+ let messagesToShare = share.messages;
5352
+ if (share.targetMessageId) {
5353
+ messagesToShare = getMessagesUpToTarget(share.messages, share.targetMessageId);
5354
+ }
4785
5355
  const newConvoId = anonymizeConvoId(share.conversationId);
4786
5356
  const result = {
4787
5357
  shareId: share.shareId || shareId,
@@ -4790,7 +5360,7 @@ function createShareMethods(mongoose) {
4790
5360
  createdAt: share.createdAt,
4791
5361
  updatedAt: share.updatedAt,
4792
5362
  conversationId: newConvoId,
4793
- messages: anonymizeMessages(share.messages, newConvoId),
5363
+ messages: anonymizeMessages(messagesToShare, newConvoId),
4794
5364
  };
4795
5365
  return result;
4796
5366
  }
@@ -4898,10 +5468,34 @@ function createShareMethods(mongoose) {
4898
5468
  throw new ShareServiceError('Error deleting shared links', 'BULK_DELETE_ERROR');
4899
5469
  }
4900
5470
  }
5471
+ /**
5472
+ * Delete shared links by conversation ID
5473
+ */
5474
+ async function deleteConvoSharedLink(user, conversationId) {
5475
+ if (!user || !conversationId) {
5476
+ throw new ShareServiceError('Missing required parameters', 'INVALID_PARAMS');
5477
+ }
5478
+ try {
5479
+ const SharedLink = mongoose.models.SharedLink;
5480
+ const result = await SharedLink.deleteMany({ user, conversationId });
5481
+ return {
5482
+ message: 'Shared links deleted successfully',
5483
+ deletedCount: result.deletedCount,
5484
+ };
5485
+ }
5486
+ catch (error) {
5487
+ logger$1.error('[deleteConvoSharedLink] Error deleting shared links', {
5488
+ error: error instanceof Error ? error.message : 'Unknown error',
5489
+ user,
5490
+ conversationId,
5491
+ });
5492
+ throw new ShareServiceError('Error deleting shared links', 'SHARE_DELETE_ERROR');
5493
+ }
5494
+ }
4901
5495
  /**
4902
5496
  * Create a new shared link for a conversation
4903
5497
  */
4904
- async function createSharedLink(user, conversationId) {
5498
+ async function createSharedLink(user, conversationId, targetMessageId) {
4905
5499
  if (!user || !conversationId) {
4906
5500
  throw new ShareServiceError('Missing required parameters', 'INVALID_PARAMS');
4907
5501
  }
@@ -4910,7 +5504,12 @@ function createShareMethods(mongoose) {
4910
5504
  const SharedLink = mongoose.models.SharedLink;
4911
5505
  const Conversation = mongoose.models.Conversation;
4912
5506
  const [existingShare, conversationMessages] = await Promise.all([
4913
- SharedLink.findOne({ conversationId, user, isPublic: true })
5507
+ SharedLink.findOne({
5508
+ conversationId,
5509
+ user,
5510
+ isPublic: true,
5511
+ ...(targetMessageId && { targetMessageId }),
5512
+ })
4914
5513
  .select('-_id -__v -user')
4915
5514
  .lean(),
4916
5515
  Message.find({ conversationId, user }).sort({ createdAt: 1 }).lean(),
@@ -4919,11 +5518,16 @@ function createShareMethods(mongoose) {
4919
5518
  logger$1.error('[createSharedLink] Share already exists', {
4920
5519
  user,
4921
5520
  conversationId,
5521
+ targetMessageId,
4922
5522
  });
4923
5523
  throw new ShareServiceError('Share already exists', 'SHARE_EXISTS');
4924
5524
  }
4925
5525
  else if (existingShare) {
4926
- await SharedLink.deleteOne({ conversationId, user });
5526
+ await SharedLink.deleteOne({
5527
+ conversationId,
5528
+ user,
5529
+ ...(targetMessageId && { targetMessageId }),
5530
+ });
4927
5531
  }
4928
5532
  const conversation = (await Conversation.findOne({ conversationId, user }).lean());
4929
5533
  // Check if user owns the conversation
@@ -4942,6 +5546,7 @@ function createShareMethods(mongoose) {
4942
5546
  messages: conversationMessages,
4943
5547
  title,
4944
5548
  user,
5549
+ ...(targetMessageId && { targetMessageId }),
4945
5550
  });
4946
5551
  return { shareId, conversationId };
4947
5552
  }
@@ -4953,6 +5558,7 @@ function createShareMethods(mongoose) {
4953
5558
  error: error instanceof Error ? error.message : 'Unknown error',
4954
5559
  user,
4955
5560
  conversationId,
5561
+ targetMessageId,
4956
5562
  });
4957
5563
  throw new ShareServiceError('Error creating shared link', 'SHARE_CREATE_ERROR');
4958
5564
  }
@@ -5065,6 +5671,7 @@ function createShareMethods(mongoose) {
5065
5671
  deleteSharedLink,
5066
5672
  getSharedMessages,
5067
5673
  deleteAllSharedLinks,
5674
+ deleteConvoSharedLink,
5068
5675
  };
5069
5676
  }
5070
5677
 
@@ -5087,5 +5694,5 @@ function createMethods(mongoose) {
5087
5694
  };
5088
5695
  }
5089
5696
 
5090
- export { RoleBits, Action as actionSchema, agentCategorySchema, agentSchema, assistantSchema, balanceSchema, bannerSchema, categoriesSchema, conversationTag as conversationTagSchema, convoSchema, createMethods, createModels, file as fileSchema, getTransactionSupport, groupSchema, hashToken, keySchema, logger$1 as logger, logger as meiliLogger, MemoryEntrySchema as memorySchema, messageSchema, pluginAuthSchema, presetSchema, projectSchema, promptGroupSchema, promptSchema, roleSchema, sessionSchema, shareSchema, signPayload, supportsTransactions, tokenSchema, toolCallSchema, transactionSchema, userSchema };
5697
+ export { AppService, RoleBits, Action as actionSchema, agentCategorySchema, agentSchema, agentsConfigSetup, assistantSchema, balanceSchema, bannerSchema, categoriesSchema, conversationTag as conversationTagSchema, convoSchema, createMethods, createModels, file as fileSchema, getTransactionSupport, getWebSearchKeys, groupSchema, hashToken, keySchema, loadDefaultInterface, loadTurnstileConfig, loadWebSearchConfig, logger$1 as logger, logger as meiliLogger, MemoryEntrySchema as memorySchema, messageSchema, pluginAuthSchema, presetSchema, processModelSpecs, projectSchema, promptGroupSchema, promptSchema, roleSchema, sessionSchema, shareSchema, signPayload, supportsTransactions, tokenSchema, toolCallSchema, transactionSchema, userSchema, webSearchAuth, webSearchKeys };
5091
5698
  //# sourceMappingURL=index.es.js.map