diff --git a/api/server/controllers/tools.js b/api/server/controllers/tools.js index 1df11b1059..437cf44cae 100644 --- a/api/server/controllers/tools.js +++ b/api/server/controllers/tools.js @@ -1,12 +1,13 @@ const { nanoid } = require('nanoid'); const { EnvVar } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); -const { checkAccess, loadWebSearchAuth } = require('@librechat/api'); +const { checkAccess, loadWebSearchAuth, createTempChatExpirationDate } = require('@librechat/api'); const { Tools, AuthType, Permissions, ToolCallTypes, + RetentionMode, PermissionTypes, } = require('librechat-data-provider'); const { getRoleByName, createToolCall, getToolCallsByConvo, getMessage } = require('~/models'); @@ -181,6 +182,15 @@ const callTool = async (req, res) => { user: req.user.id, }; + if (req?.body?.isTemporary || appConfig?.interfaceConfig?.retentionMode === RetentionMode.ALL) { + try { + toolCallData.expiredAt = createTempChatExpirationDate(appConfig?.interfaceConfig); + } catch (err) { + logger.error('Error creating tool call expiration date:', err); + toolCallData.expiredAt = null; + } + } + if (!artifact || !artifact.files || toolId !== Tools.execute_code) { createToolCall(toolCallData).catch((error) => { logger.error(`Error creating tool call: ${error.message}`); diff --git a/api/server/routes/share.js b/api/server/routes/share.js index 296644afde..d2c992b27b 100644 --- a/api/server/routes/share.js +++ b/api/server/routes/share.js @@ -1,6 +1,7 @@ const express = require('express'); -const { isEnabled } = require('@librechat/api'); +const { isEnabled, createTempChatExpirationDate } = require('@librechat/api'); const { logger } = require('@librechat/data-schemas'); +const { RetentionMode } = require('librechat-data-provider'); const { getSharedMessages, createSharedLink, @@ -98,7 +99,20 @@ router.get('/link/:conversationId', requireJwtAuth, async (req, res) => { router.post('/:conversationId', requireJwtAuth, async (req, res) => { try { const { targetMessageId } = req.body; - const created = await createSharedLink(req.user.id, req.params.conversationId, targetMessageId); + let expiredAt; + if (req?.config?.interfaceConfig?.retentionMode === RetentionMode.ALL) { + try { + expiredAt = createTempChatExpirationDate(req.config?.interfaceConfig); + } catch (err) { + logger.error('Error creating shared link expiration date:', err); + } + } + const created = await createSharedLink( + req.user.id, + req.params.conversationId, + targetMessageId, + expiredAt, + ); if (created) { res.status(200).json(created); } else { @@ -112,7 +126,15 @@ router.post('/:conversationId', requireJwtAuth, async (req, res) => { router.patch('/:shareId', requireJwtAuth, async (req, res) => { try { - const updatedShare = await updateSharedLink(req.user.id, req.params.shareId); + let expiredAt; + if (req?.config?.interfaceConfig?.retentionMode === RetentionMode.ALL) { + try { + expiredAt = createTempChatExpirationDate(req.config?.interfaceConfig); + } catch (err) { + logger.error('Error creating shared link expiration date:', err); + } + } + const updatedShare = await updateSharedLink(req.user.id, req.params.shareId, expiredAt); if (updatedShare) { res.status(200).json(updatedShare); } else { diff --git a/api/server/services/Files/Code/process.js b/api/server/services/Files/Code/process.js index 7cdebeb202..55eb56d15a 100644 --- a/api/server/services/Files/Code/process.js +++ b/api/server/services/Files/Code/process.js @@ -27,6 +27,7 @@ const { filterFilesByAgentAccess } = require('~/server/services/Files/permission const { createFile, getFiles, updateFile, claimCodeFile } = require('~/models'); const { getStrategyFunctions } = require('~/server/services/Files/strategies'); const { convertImage } = require('~/server/services/Files/images/convert'); +const { getRetentionExpiry } = require('~/server/services/Files/process'); const { determineFileType } = require('~/server/utils'); const axios = createAxiosInstance(); @@ -182,6 +183,7 @@ const processCodeOutput = async ({ source: appConfig.fileStrategy, context: FileContext.execute_code, metadata: { fileIdentifier }, + ...getRetentionExpiry(req), }; await createFile(file, true); return Object.assign(file, { messageId, toolCallId }); @@ -241,6 +243,7 @@ const processCodeOutput = async ({ context: FileContext.execute_code, usage: isUpdate ? (claimed.usage ?? 0) + 1 : 1, createdAt: isUpdate ? claimed.createdAt : formattedDate, + ...getRetentionExpiry(req), }; await createFile(file, true); diff --git a/api/server/services/Files/process.js b/api/server/services/Files/process.js index f7d7731975..ce3eff643a 100644 --- a/api/server/services/Files/process.js +++ b/api/server/services/Files/process.js @@ -8,6 +8,7 @@ const { FileContext, FileSources, imageExtRegex, + RetentionMode, EModelEndpoint, EToolResources, mergeFileConfig, @@ -20,7 +21,12 @@ const { } = require('librechat-data-provider'); const { EnvVar } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); -const { sanitizeFilename, parseText, processAudioFile } = require('@librechat/api'); +const { + sanitizeFilename, + parseText, + processAudioFile, + createTempChatExpirationDate, +} = require('@librechat/api'); const { convertImage, resizeAndConvert, @@ -37,6 +43,23 @@ const { determineFileType } = require('~/server/utils'); const { STTService } = require('./Audio/STTService'); const db = require('~/models'); +/** + * Returns `{ expiredAt }` when the request indicates data retention applies, otherwise `{}`. + * Spread into file data objects before calling createFile. + * @param {ServerRequest} req + * @returns {{ expiredAt?: Date }} + */ +function getRetentionExpiry(req) { + if (req?.body?.isTemporary || req?.config?.interfaceConfig?.retentionMode === RetentionMode.ALL) { + try { + return { expiredAt: createTempChatExpirationDate(req.config?.interfaceConfig) }; + } catch (_err) { + return {}; + } + } + return {}; +} + /** * Creates a modular file upload wrapper that ensures filename sanitization * across all storage strategies. This prevents storage-specific implementations @@ -307,6 +330,7 @@ const processImageFile = async ({ req, res, metadata, returnFile = false }) => { context: FileContext.message_attachment, source, type: `image/${appConfig.imageOutputType}`, + ...getRetentionExpiry(req), width, height, }, @@ -359,6 +383,7 @@ const uploadImageBuffer = async ({ req, context, metadata = {}, resize = true }) source, type, width, + ...getRetentionExpiry(req), height, }, true, @@ -445,6 +470,7 @@ const processFileUpload = async ({ req, res, metadata }) => { context: isAssistantUpload ? FileContext.assistants : FileContext.message_attachment, model: isAssistantUpload ? req.body.model : undefined, type: file.mimetype, + ...getRetentionExpiry(req), embedded, source, height, @@ -541,6 +567,7 @@ const processAgentFileUpload = async ({ req, res, metadata }) => { filename: file.originalname, model: messageAttachment ? undefined : req.body.model, context: messageAttachment ? FileContext.message_attachment : FileContext.agents, + ...getRetentionExpiry(req), }); if (!messageAttachment && tool_resource) { @@ -717,6 +744,7 @@ const processAgentFileUpload = async ({ req, res, metadata }) => { source, height, width, + ...getRetentionExpiry(req), }); const result = await db.createFile(fileInfo, true); @@ -762,6 +790,7 @@ const processOpenAIFile = async ({ source, model: openai.req.body.model, filename: originalName ?? file_id, + ...getRetentionExpiry(openai.req), }; if (saveFile) { @@ -805,6 +834,7 @@ const processOpenAIImageOutput = async ({ req, buffer, file_id, filename, fileEx context: FileContext.assistants_output, file_id, filename, + ...getRetentionExpiry(req), }; db.createFile(file, true); return file; @@ -961,6 +991,7 @@ async function saveBase64Image( user: req.user.id, bytes: image.bytes, width: image.width, + ...getRetentionExpiry(req), height: image.height, }, true, @@ -1047,6 +1078,7 @@ function filterFile({ req, image, isAvatar }) { module.exports = { filterFile, + getRetentionExpiry, processFileURL, saveBase64Image, processImageFile, diff --git a/client/src/components/Chat/Menus/BookmarkMenu.tsx b/client/src/components/Chat/Menus/BookmarkMenu.tsx index d66fccd24b..d7567b2019 100644 --- a/client/src/components/Chat/Menus/BookmarkMenu.tsx +++ b/client/src/components/Chat/Menus/BookmarkMenu.tsx @@ -26,8 +26,9 @@ const BookmarkMenu: FC = () => { const conversationId = conversation?.conversationId ?? ''; const updateConvoTags = useBookmarkSuccess(conversationId); const tags = conversation?.tags; - const isTemporary = conversation?.expiredAt != null; - + const isTemporary = + conversation.isTemporary || + (conversation.isTemporary === undefined && conversation.expiredAt != null); const menuId = useId(); const [isMenuOpen, setIsMenuOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false); @@ -60,9 +61,9 @@ const BookmarkMenu: FC = () => { const isActiveConvo = Boolean( conversation && - conversationId && - conversationId !== Constants.NEW_CONVO && - conversationId !== 'search', + conversationId && + conversationId !== Constants.NEW_CONVO && + conversationId !== 'search', ); const handleSubmit = useCallback( diff --git a/client/src/routes/ChatRoute.tsx b/client/src/routes/ChatRoute.tsx index a17d349037..10a05ee56c 100644 --- a/client/src/routes/ChatRoute.tsx +++ b/client/src/routes/ChatRoute.tsx @@ -62,7 +62,10 @@ export default function ChatRoute() { const endpointsQuery = useGetEndpointsQuery({ enabled: isAuthenticated }); const assistantListMap = useAssistantListMap(); - const isTemporaryChat = conversation && conversation.expiredAt ? true : false; + const isTemporaryChat = + conversation && + (conversation.isTemporary || + (conversation.isTemporary === undefined && conversation.expiredAt != null)); useEffect(() => { if (conversationId === Constants.NEW_CONVO) { diff --git a/librechat.example.yaml b/librechat.example.yaml index 92206c4b6e..671c653534 100644 --- a/librechat.example.yaml +++ b/librechat.example.yaml @@ -131,6 +131,7 @@ interface: # Temporary chat retention period in hours (default: 720, min: 1, max: 8760) # temporaryChatRetention: 1 + retentionMode: "temporary" # Example Cloudflare turnstile (optional) #turnstile: diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index ca40ec2c8c..3d339a403b 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -44,6 +44,7 @@ export const excludedKeys = new Set([ 'createdAt', 'updatedAt', 'expiredAt', + 'isTemporary', 'messages', 'isArchived', 'tags', @@ -679,6 +680,11 @@ const mcpServersSchema = z export type TMcpServersConfig = z.infer; +export enum RetentionMode { + ALL = 'all', + TEMPORARY = 'temporary', +} + export const interfaceSchema = z .object({ privacyPolicy: z @@ -720,6 +726,7 @@ export const interfaceSchema = z .optional(), temporaryChat: z.boolean().optional(), temporaryChatRetention: z.number().min(1).max(8760).optional(), + retentionMode: z.nativeEnum(RetentionMode).default(RetentionMode.TEMPORARY), runCode: z.boolean().optional(), webSearch: z.boolean().optional(), peoplePicker: z diff --git a/packages/data-provider/src/schemas.ts b/packages/data-provider/src/schemas.ts index 084f74af86..b677222c11 100644 --- a/packages/data-provider/src/schemas.ts +++ b/packages/data-provider/src/schemas.ts @@ -797,6 +797,7 @@ export const tConversationSchema = z.object({ iconURL: z.string().nullable().optional(), /* temporary chat */ expiredAt: z.string().nullable().optional(), + isTemporary: z.boolean().optional(), /* file token limits */ fileTokenLimit: coerceNumber.optional(), /** @deprecated */ diff --git a/packages/data-schemas/src/app/interface.ts b/packages/data-schemas/src/app/interface.ts index 3cd71cfb20..60ac929b53 100644 --- a/packages/data-schemas/src/app/interface.ts +++ b/packages/data-schemas/src/app/interface.ts @@ -46,6 +46,8 @@ export async function loadDefaultInterface({ multiConvo: interfaceConfig?.multiConvo, agents: interfaceConfig?.agents, temporaryChat: interfaceConfig?.temporaryChat, + temporaryChatRetention: interfaceConfig?.temporaryChatRetention, + retentionMode: interfaceConfig?.retentionMode, runCode: interfaceConfig?.runCode, webSearch: interfaceConfig?.webSearch, fileSearch: interfaceConfig?.fileSearch, diff --git a/packages/data-schemas/src/methods/conversation.spec.ts b/packages/data-schemas/src/methods/conversation.spec.ts index 9e4c2d2f5d..b045486291 100644 --- a/packages/data-schemas/src/methods/conversation.spec.ts +++ b/packages/data-schemas/src/methods/conversation.spec.ts @@ -398,13 +398,24 @@ describe('Conversation Operations', () => { expect(secondSave?.expiredAt).toBeNull(); }); - it('should filter out expired conversations in getConvosByCursor', async () => { + it('should filter out temporary conversations in getConvosByCursor', async () => { // Create some test conversations - const nonExpiredConvo = await Conversation.create({ + const newNonTemporaryConvo = await Conversation.create({ conversationId: uuidv4(), user: 'user123', - title: 'Non-expired', + title: 'New Non-temporary Conversation', endpoint: EModelEndpoint.openAI, + isTemporary: false, + expiredAt: new Date(Date.now() + 24 * 60 * 60 * 1000), + updatedAt: new Date(), + }); + + const oldNonTemporaryConvo = await Conversation.create({ + conversationId: uuidv4(), + user: 'user123', + title: 'Old Non-Temporary Conversation', + endpoint: EModelEndpoint.openAI, + isTemporary: undefined, expiredAt: null, updatedAt: new Date(), }); @@ -412,9 +423,10 @@ describe('Conversation Operations', () => { await Conversation.create({ conversationId: uuidv4(), user: 'user123', - title: 'Future expired', + title: 'Temporary conversation', endpoint: EModelEndpoint.openAI, - expiredAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours from now + isTemporary: true, + expiredAt: new Date(Date.now() + 24 * 60 * 60 * 1000), updatedAt: new Date(), }); @@ -423,41 +435,61 @@ describe('Conversation Operations', () => { const result = await getConvosByCursor('user123'); - // Should only return conversations with null or non-existent expiredAt - expect(result?.conversations).toHaveLength(1); - expect(result?.conversations[0]?.conversationId).toBe(nonExpiredConvo.conversationId); + // Should return both non-temporary conversations, not the temporary one + expect(result?.conversations).toHaveLength(2); + const convoIds = result?.conversations.map((c) => c.conversationId); + expect(convoIds).toContain(newNonTemporaryConvo.conversationId); + expect(convoIds).toContain(oldNonTemporaryConvo.conversationId); }); it('should filter out expired conversations in getConvosQueried', async () => { - // Create test conversations - const nonExpiredConvo = await Conversation.create({ + const newNonTemporaryConvo = await Conversation.create({ conversationId: uuidv4(), user: 'user123', - title: 'Non-expired', + title: 'New Non-temporary Conversation', endpoint: EModelEndpoint.openAI, - expiredAt: null, + isTemporary: false, + expiredAt: new Date(Date.now() + 24 * 60 * 60 * 1000), + updatedAt: new Date(), }); - const expiredConvo = await Conversation.create({ + const oldNonTemporaryConvo = await Conversation.create({ conversationId: uuidv4(), user: 'user123', - title: 'Expired', + title: 'Old Non-Temporary Conversation', endpoint: EModelEndpoint.openAI, + isTemporary: undefined, + expiredAt: null, + updatedAt: new Date(), + }); + + const tempConvo = await Conversation.create({ + conversationId: uuidv4(), + user: 'user123', + title: 'Temporary conversation', + endpoint: EModelEndpoint.openAI, + isTemporary: true, expiredAt: new Date(Date.now() + 24 * 60 * 60 * 1000), + updatedAt: new Date(), }); const convoIds = [ - { conversationId: nonExpiredConvo.conversationId }, - { conversationId: expiredConvo.conversationId }, + { conversationId: newNonTemporaryConvo.conversationId }, + { conversationId: oldNonTemporaryConvo.conversationId }, + { conversationId: tempConvo.conversationId }, ]; const result = await getConvosQueried('user123', convoIds); - // Should only return the non-expired conversation - expect(result?.conversations).toHaveLength(1); - expect(result?.conversations[0].conversationId).toBe(nonExpiredConvo.conversationId); - expect(result?.convoMap[nonExpiredConvo.conversationId]).toBeDefined(); - expect(result?.convoMap[expiredConvo.conversationId]).toBeUndefined(); + // Should only return the non-temporary conversations + expect(result?.conversations).toHaveLength(2); + + const resultIds = result?.conversations.map((c) => c.conversationId); + expect(resultIds).toContain(newNonTemporaryConvo.conversationId); + expect(resultIds).toContain(oldNonTemporaryConvo.conversationId); + expect(result?.convoMap[newNonTemporaryConvo.conversationId]).toBeDefined(); + expect(result?.convoMap[oldNonTemporaryConvo.conversationId]).toBeDefined(); + expect(result?.convoMap[tempConvo.conversationId]).toBeUndefined(); }); }); diff --git a/packages/data-schemas/src/methods/conversation.ts b/packages/data-schemas/src/methods/conversation.ts index 00b5cfee7a..057828bb40 100644 --- a/packages/data-schemas/src/methods/conversation.ts +++ b/packages/data-schemas/src/methods/conversation.ts @@ -1,4 +1,5 @@ import type { FilterQuery, Model, SortOrder } from 'mongoose'; +import { RetentionMode } from 'librechat-data-provider'; import { createTempChatExpirationDate } from '~/utils/tempChatRetention'; import { tenantSafeBulkWrite } from '~/utils/tenantBulkWrite'; import logger from '~/config/winston'; @@ -174,6 +175,12 @@ export function createConversationMethods( } if (isTemporary) { + update.isTemporary = true; + } else { + update.isTemporary = false; + } + + if (isTemporary || interfaceConfig?.retentionMode === RetentionMode.ALL) { try { update.expiredAt = createTempChatExpirationDate(interfaceConfig); } catch (err) { @@ -278,7 +285,7 @@ export function createConversationMethods( } filters.push({ - $or: [{ expiredAt: null }, { expiredAt: { $exists: false } }], + $or: [{ isTemporary: false }, { isTemporary: { $exists: false } }], } as FilterQuery); if (search) { @@ -399,7 +406,7 @@ export function createConversationMethods( const results = await Conversation.find({ user, conversationId: { $in: conversationIds }, - $or: [{ expiredAt: { $exists: false } }, { expiredAt: null }], + $or: [{ isTemporary: false }, { isTemporary: { $exists: false } }], }).lean(); results.sort( diff --git a/packages/data-schemas/src/methods/message.ts b/packages/data-schemas/src/methods/message.ts index 2e638b6bfb..6dc3966330 100644 --- a/packages/data-schemas/src/methods/message.ts +++ b/packages/data-schemas/src/methods/message.ts @@ -1,4 +1,5 @@ import type { DeleteResult, FilterQuery, Model } from 'mongoose'; +import { RetentionMode } from 'librechat-data-provider'; import logger from '~/config/winston'; import { createTempChatExpirationDate } from '~/utils/tempChatRetention'; import { tenantSafeBulkWrite } from '~/utils/tenantBulkWrite'; @@ -91,7 +92,7 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa messageId: params.newMessageId || params.messageId, }; - if (isTemporary) { + if (isTemporary || interfaceConfig?.retentionMode === RetentionMode.ALL) { try { update.expiredAt = createTempChatExpirationDate(interfaceConfig); } catch (err) { diff --git a/packages/data-schemas/src/methods/share.ts b/packages/data-schemas/src/methods/share.ts index 2a0d2bc3bd..fe3a0c8607 100644 --- a/packages/data-schemas/src/methods/share.ts +++ b/packages/data-schemas/src/methods/share.ts @@ -345,6 +345,7 @@ export function createShareMethods(mongoose: typeof import('mongoose')) { user: string, conversationId: string, targetMessageId?: string, + expiredAt?: Date, ): Promise { if (!user || !conversationId) { throw new ShareServiceError('Missing required parameters', 'INVALID_PARAMS'); @@ -408,6 +409,7 @@ export function createShareMethods(mongoose: typeof import('mongoose')) { title, user, ...(targetMessageId && { targetMessageId }), + ...(expiredAt && { expiredAt }), }); return { shareId, conversationId }; @@ -460,7 +462,11 @@ export function createShareMethods(mongoose: typeof import('mongoose')) { /** * Update a shared link with new messages */ - async function updateSharedLink(user: string, shareId: string): Promise { + async function updateSharedLink( + user: string, + shareId: string, + expiredAt?: Date, + ): Promise { if (!user || !shareId) { throw new ShareServiceError('Missing required parameters', 'INVALID_PARAMS'); } @@ -485,6 +491,7 @@ export function createShareMethods(mongoose: typeof import('mongoose')) { messages: updatedMessages, user, shareId: newShareId, + ...(expiredAt && { expiredAt }), }; const updatedShare = (await SharedLink.findOneAndUpdate({ shareId, user }, update, { diff --git a/packages/data-schemas/src/schema/convo.ts b/packages/data-schemas/src/schema/convo.ts index c8f394935a..6b845d6ce9 100644 --- a/packages/data-schemas/src/schema/convo.ts +++ b/packages/data-schemas/src/schema/convo.ts @@ -21,6 +21,10 @@ const convoSchema: Schema = new Schema( meiliIndex: true, }, messages: [{ type: Schema.Types.ObjectId, ref: 'Message' }], + isTemporary: { + type: Boolean, + default: false, + }, ...conversationPreset, agent_id: { type: String, diff --git a/packages/data-schemas/src/schema/file.ts b/packages/data-schemas/src/schema/file.ts index e483541bdb..c8c9dbbc15 100644 --- a/packages/data-schemas/src/schema/file.ts +++ b/packages/data-schemas/src/schema/file.ts @@ -82,12 +82,16 @@ const file: Schema = new Schema( type: String, index: true, }, + expiredAt: { + type: Date, + }, }, { timestamps: true, }, ); +file.index({ expiredAt: 1 }, { expireAfterSeconds: 0 }); file.index({ createdAt: 1, updatedAt: 1 }); file.index( { filename: 1, conversationId: 1, context: 1, tenantId: 1 }, diff --git a/packages/data-schemas/src/schema/share.ts b/packages/data-schemas/src/schema/share.ts index 3238084889..616536fe52 100644 --- a/packages/data-schemas/src/schema/share.ts +++ b/packages/data-schemas/src/schema/share.ts @@ -8,6 +8,7 @@ export interface ISharedLink extends Document { shareId?: string; targetMessageId?: string; isPublic: boolean; + expiredAt?: Date; createdAt?: Date; updatedAt?: Date; tenantId?: string; @@ -45,10 +46,14 @@ const shareSchema: Schema = new Schema( type: String, index: true, }, + expiredAt: { + type: Date, + }, }, { timestamps: true }, ); +shareSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 }); shareSchema.index({ conversationId: 1, user: 1, targetMessageId: 1, tenantId: 1 }); export default shareSchema; diff --git a/packages/data-schemas/src/schema/toolCall.ts b/packages/data-schemas/src/schema/toolCall.ts index d36d6b758a..cde2163ca3 100644 --- a/packages/data-schemas/src/schema/toolCall.ts +++ b/packages/data-schemas/src/schema/toolCall.ts @@ -10,6 +10,7 @@ export interface IToolCallData extends Document { attachments?: TAttachment[]; blockIndex?: number; partIndex?: number; + expiredAt?: Date; createdAt?: Date; updatedAt?: Date; tenantId?: string; @@ -50,10 +51,14 @@ const toolCallSchema: Schema = new Schema( type: String, index: true, }, + expiredAt: { + type: Date, + }, }, { timestamps: true }, ); +toolCallSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 }); toolCallSchema.index({ messageId: 1, user: 1, tenantId: 1 }); toolCallSchema.index({ conversationId: 1, user: 1, tenantId: 1 }); diff --git a/packages/data-schemas/src/types/convo.ts b/packages/data-schemas/src/types/convo.ts index c7888efba2..5cd828e9d6 100644 --- a/packages/data-schemas/src/types/convo.ts +++ b/packages/data-schemas/src/types/convo.ts @@ -6,6 +6,7 @@ export interface IConversation extends Document { title?: string; user?: string; messages?: Types.ObjectId[]; + isTemporary?: boolean; // Fields provided by conversationPreset (adjust types as needed) endpoint?: string; endpointType?: string; diff --git a/packages/data-schemas/src/types/file.ts b/packages/data-schemas/src/types/file.ts index bbf9de3d3d..57131fdb98 100644 --- a/packages/data-schemas/src/types/file.ts +++ b/packages/data-schemas/src/types/file.ts @@ -23,6 +23,7 @@ export interface IMongoFile extends Omit { fileIdentifier?: string; }; expiresAt?: Date; + expiredAt?: Date; createdAt?: Date; updatedAt?: Date; tenantId?: string; diff --git a/packages/data-schemas/src/types/share.ts b/packages/data-schemas/src/types/share.ts index 8b54990cf4..dc0203cb93 100644 --- a/packages/data-schemas/src/types/share.ts +++ b/packages/data-schemas/src/types/share.ts @@ -10,6 +10,7 @@ export interface ISharedLink { shareId?: string; targetMessageId?: string; isPublic: boolean; + expiredAt?: Date; createdAt?: Date; updatedAt?: Date; }