diff --git a/api/server/routes/__tests__/share.spec.js b/api/server/routes/__tests__/share.spec.js
index c9d6cb0037..1d20d224af 100644
--- a/api/server/routes/__tests__/share.spec.js
+++ b/api/server/routes/__tests__/share.spec.js
@@ -35,27 +35,29 @@ jest.mock('@librechat/data-schemas', () => ({
SYSTEM_TENANT_ID: '__SYSTEM__',
}));
-jest.mock('librechat-data-provider', () => ({
- PermissionTypes: {
- SHARED_LINKS: 'SHARED_LINKS',
- },
- Permissions: {
- CREATE: 'CREATE',
- SHARE_PUBLIC: 'SHARE_PUBLIC',
- },
- RetentionMode: {
- ALL: 'all',
- TEMPORARY: 'temporary',
- },
- FileSources: {
- local: 'local',
- s3: 's3',
- cloudfront: 'cloudfront',
- azure_blob: 'azure_blob',
- firebase: 'firebase',
- text: 'text',
- },
-}));
+jest.mock('librechat-data-provider', () => {
+ const RetentionMode = { ALL: 'all', TEMPORARY: 'temporary', EPHEMERAL: 'ephemeral' };
+ return {
+ PermissionTypes: {
+ SHARED_LINKS: 'SHARED_LINKS',
+ },
+ Permissions: {
+ CREATE: 'CREATE',
+ SHARE_PUBLIC: 'SHARE_PUBLIC',
+ },
+ RetentionMode,
+ isAllDataRetention: (mode) => mode === RetentionMode.ALL || mode === RetentionMode.EPHEMERAL,
+ isForcedTemporaryRetention: (mode) => mode === RetentionMode.EPHEMERAL,
+ FileSources: {
+ local: 'local',
+ s3: 's3',
+ cloudfront: 'cloudfront',
+ azure_blob: 'azure_blob',
+ firebase: 'firebase',
+ text: 'text',
+ },
+ };
+});
jest.mock('mongoose', () => ({
models: {
diff --git a/api/server/utils/import/importBatchBuilder.js b/api/server/utils/import/importBatchBuilder.js
index b1856737cd..c382888b82 100644
--- a/api/server/utils/import/importBatchBuilder.js
+++ b/api/server/utils/import/importBatchBuilder.js
@@ -7,8 +7,9 @@ const {
const {
EModelEndpoint,
Constants,
- RetentionMode,
openAISettings,
+ isAllDataRetention,
+ isForcedTemporaryRetention,
} = require('librechat-data-provider');
const { bulkIncrementTagCounts, bulkSaveConvos, bulkSaveMessages } = require('~/models');
const { FALLBACK_MODEL_BY_ENDPOINT } = require('./defaults');
@@ -45,19 +46,20 @@ class ImportBatchBuilder {
return this.retentionFields;
}
- if (this.interfaceConfig?.retentionMode !== RetentionMode.ALL) {
+ if (!isAllDataRetention(this.interfaceConfig?.retentionMode)) {
this.retentionFields = {};
return this.retentionFields;
}
+ const isTemporary = isForcedTemporaryRetention(this.interfaceConfig?.retentionMode);
try {
this.retentionFields = {
- isTemporary: false,
+ isTemporary,
expiredAt: createTempChatExpirationDate(this.interfaceConfig),
};
} catch (error) {
logger.error('[ImportBatchBuilder] Error creating import expiration date:', error);
- this.retentionFields = { isTemporary: false, expiredAt: createFallbackRetentionDate() };
+ this.retentionFields = { isTemporary, expiredAt: createFallbackRetentionDate() };
}
return this.retentionFields;
}
diff --git a/api/server/utils/import/importers.spec.js b/api/server/utils/import/importers.spec.js
index a9bd679f55..46e87889e0 100644
--- a/api/server/utils/import/importers.spec.js
+++ b/api/server/utils/import/importers.spec.js
@@ -1145,6 +1145,23 @@ describe('importLibreChatConvo', () => {
expect(result.conversation.expiredAt).toBeInstanceOf(Date);
expect(result.conversation.expiredAt).toBe(message.expiredAt);
});
+
+ it('marks imported conversations and messages temporary under ephemeral retention', () => {
+ const requestUserId = 'user-123';
+ const builder = new ImportBatchBuilder(requestUserId, {
+ retentionMode: RetentionMode.EPHEMERAL,
+ temporaryChatRetention: 24,
+ });
+ builder.startConversation(EModelEndpoint.openAI);
+ const message = builder.addUserMessage('Ephemeral import');
+ const result = builder.finishConversation('Imported ephemeral chat');
+
+ expect(message.isTemporary).toBe(true);
+ expect(message.expiredAt).toBeInstanceOf(Date);
+ expect(result.conversation.isTemporary).toBe(true);
+ expect(result.conversation.expiredAt).toBeInstanceOf(Date);
+ expect(result.conversation.expiredAt).toBe(message.expiredAt);
+ });
});
});
diff --git a/client/src/components/Chat/TemporaryChat.tsx b/client/src/components/Chat/TemporaryChat.tsx
index 39d42462bc..9a083c65e3 100644
--- a/client/src/components/Chat/TemporaryChat.tsx
+++ b/client/src/components/Chat/TemporaryChat.tsx
@@ -3,24 +3,32 @@ import { useRecoilValue } from 'recoil';
import { TooltipAnchor } from '@librechat/client';
import { MessageCircleDashed } from 'lucide-react';
import { useRecoilState, useRecoilCallback } from 'recoil';
+import { isForcedTemporaryRetention } from 'librechat-data-provider';
import { useShortcutAriaKey, useShortcutHint } from '~/hooks/useKeyboardShortcuts';
+import { useGetStartupConfig } from '~/data-provider';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
import store from '~/store';
export function TemporaryChat() {
const localize = useLocalize();
+ const { data: startupConfig } = useGetStartupConfig();
const [isTemporary, setIsTemporary] = useRecoilState(store.isTemporary);
const conversation = useRecoilValue(store.conversationByIndex(0));
const isSubmitting = useRecoilValue(store.isSubmittingFamily(0));
const tooltipDescription = useShortcutHint('toggleTemporaryChat', localize('com_ui_temporary'));
const ariaKey = useShortcutAriaKey('toggleTemporaryChat');
+ const isEnforced = isForcedTemporaryRetention(startupConfig?.interface?.retentionMode);
+
const handleBadgeToggle = useRecoilCallback(
() => () => {
+ if (isEnforced) {
+ return;
+ }
setIsTemporary(!isTemporary);
},
- [isTemporary],
+ [isTemporary, isEnforced],
);
if (
@@ -30,21 +38,26 @@ export function TemporaryChat() {
return null;
}
+ const isActive = isEnforced || isTemporary;
+ const label = isEnforced ? localize('com_ui_temporary_enforced') : localize('com_ui_temporary');
+
return (
diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json
index 84e8dcc38d..d14be60f00 100644
--- a/client/src/locales/en/translation.json
+++ b/client/src/locales/en/translation.json
@@ -1795,6 +1795,7 @@
"com_ui_support_contact_name_placeholder": "Support contact name",
"com_ui_teach_or_explain": "Learning",
"com_ui_temporary": "Temporary Chat",
+ "com_ui_temporary_enforced": "Temporary Chat is enabled for all chats by your administrator",
"com_ui_terms_and_conditions": "Terms and Conditions",
"com_ui_terms_of_service": "Terms of service",
"com_ui_text_variables": "Text variables",
diff --git a/client/src/routes/ChatRoute.tsx b/client/src/routes/ChatRoute.tsx
index 5f55df51dd..325ae89e98 100644
--- a/client/src/routes/ChatRoute.tsx
+++ b/client/src/routes/ChatRoute.tsx
@@ -3,8 +3,8 @@ import { useQueryClient } from '@tanstack/react-query';
import { useRecoilCallback, useRecoilValue } from 'recoil';
import { Spinner, useToastContext } from '@librechat/client';
import { useParams, useSearchParams } from 'react-router-dom';
-import { Constants, EModelEndpoint } from 'librechat-data-provider';
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
+import { Constants, EModelEndpoint, isForcedTemporaryRetention } from 'librechat-data-provider';
import type { TPreset } from 'librechat-data-provider';
import {
mergeQuerySettingsWithSpec,
@@ -126,16 +126,17 @@ export default function ChatRoute() {
const assistantListMap = useAssistantListMap();
const isTemporaryChat = isTemporaryConversation(conversation);
+ const forceTemporaryChat = isForcedTemporaryRetention(startupConfig?.interface?.retentionMode);
useEffect(() => {
if (conversationId === Constants.NEW_CONVO) {
- setIsTemporary(defaultTemporaryChat);
- } else if (isTemporaryChat) {
- setIsTemporary(isTemporaryChat);
+ setIsTemporary(forceTemporaryChat || defaultTemporaryChat);
+ } else if (forceTemporaryChat || isTemporaryChat) {
+ setIsTemporary(true);
} else {
setIsTemporary(false);
}
- }, [conversationId, isTemporaryChat, setIsTemporary, defaultTemporaryChat]);
+ }, [conversationId, isTemporaryChat, setIsTemporary, defaultTemporaryChat, forceTemporaryChat]);
/** This effect is mainly for the first conversation state change on first load of the page.
* Adjusting this may have unintended consequences on the conversation state.
diff --git a/librechat.example.yaml b/librechat.example.yaml
index bfdcc60148..8fa7997a20 100644
--- a/librechat.example.yaml
+++ b/librechat.example.yaml
@@ -214,8 +214,10 @@ interface:
# Temporary chat retention period in hours (default: 720, min: 1, max: 8760)
# temporaryChatRetention: 1
- # Retention mode: "all" applies expiry to all data types, "temporary" (default) only to temporary chats
- # Before switching from "all" back to "temporary", remove retention deadlines from non-temporary data
+ # Retention mode: "temporary" (default) applies expiry only to chats users mark temporary;
+ # "all" applies expiry to all data types while keeping chats visible in history;
+ # "ephemeral" forces every chat to be temporary (always on, not user-toggleable) and applies expiry to all data.
+ # Before switching from "all"/"ephemeral" back to "temporary", remove retention deadlines from non-temporary data
# that should stop expiring:
# db.conversations.updateMany({ isTemporary: false, expiredAt: { $ne: null } }, { $unset: { expiredAt: 1 } })
# db.messages.updateMany({ isTemporary: false, expiredAt: { $ne: null } }, { $unset: { expiredAt: 1 } })
diff --git a/packages/api/src/app/permissions.spec.ts b/packages/api/src/app/permissions.spec.ts
index b93efe64d3..a888d9bd6f 100644
--- a/packages/api/src/app/permissions.spec.ts
+++ b/packages/api/src/app/permissions.spec.ts
@@ -1,5 +1,11 @@
import { loadDefaultInterface } from '@librechat/data-schemas';
-import { SystemRoles, Permissions, PermissionTypes, roleDefaults } from 'librechat-data-provider';
+import {
+ SystemRoles,
+ Permissions,
+ roleDefaults,
+ RetentionMode,
+ PermissionTypes,
+} from 'librechat-data-provider';
import type { TConfigDefaults, TCustomConfig } from 'librechat-data-provider';
import type { AppConfig } from '@librechat/data-schemas';
import { updateInterfacePermissions } from './permissions';
@@ -196,6 +202,38 @@ describe('updateInterfacePermissions - permissions', () => {
);
});
+ it('forces TEMPORARY_CHAT use on for all roles when retentionMode is ephemeral, even without temporaryChat config', async () => {
+ const config = {
+ interface: {
+ retentionMode: RetentionMode.EPHEMERAL,
+ },
+ };
+ const configDefaults = { interface: {} } as TConfigDefaults;
+ const interfaceConfig = await loadDefaultInterface({ config, configDefaults });
+ const appConfig = { config, interfaceConfig } as unknown as AppConfig;
+
+ await updateInterfacePermissions({
+ appConfig,
+ getRoleByName: mockGetRoleByName,
+ updateAccessPermissions: mockUpdateAccessPermissions,
+ });
+
+ expect(mockUpdateAccessPermissions).toHaveBeenCalledWith(
+ SystemRoles.USER,
+ expect.objectContaining({
+ [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
+ }),
+ null,
+ );
+ expect(mockUpdateAccessPermissions).toHaveBeenCalledWith(
+ SystemRoles.ADMIN,
+ expect.objectContaining({
+ [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
+ }),
+ null,
+ );
+ });
+
it('should call updateAccessPermissions with false when permission types are false', async () => {
const config = {
interface: {
diff --git a/packages/api/src/app/permissions.ts b/packages/api/src/app/permissions.ts
index dcc626143c..3c9c127bce 100644
--- a/packages/api/src/app/permissions.ts
+++ b/packages/api/src/app/permissions.ts
@@ -5,6 +5,7 @@ import {
roleDefaults,
PermissionTypes,
getConfigDefaults,
+ isForcedTemporaryRetention,
} from 'librechat-data-provider';
import type { IRole, AppConfig } from '@librechat/data-schemas';
import { isMemoryEnabled } from '~/memory/config';
@@ -28,7 +29,10 @@ function hasExplicitConfig(
case PermissionTypes.AGENTS:
return interfaceConfig?.agents !== undefined;
case PermissionTypes.TEMPORARY_CHAT:
- return interfaceConfig?.temporaryChat !== undefined;
+ return (
+ interfaceConfig?.temporaryChat !== undefined ||
+ isForcedTemporaryRetention(interfaceConfig?.retentionMode)
+ );
case PermissionTypes.RUN_CODE:
return interfaceConfig?.runCode !== undefined;
case PermissionTypes.WEB_SEARCH:
@@ -317,11 +321,13 @@ export async function updateInterfacePermissions({
: {}),
},
[PermissionTypes.TEMPORARY_CHAT]: {
- [Permissions.USE]: getPermissionValue(
- loadedInterface.temporaryChat,
- defaultPerms[PermissionTypes.TEMPORARY_CHAT]?.[Permissions.USE],
- defaults.temporaryChat,
- ),
+ [Permissions.USE]:
+ isForcedTemporaryRetention(loadedInterface.retentionMode) ||
+ getPermissionValue(
+ loadedInterface.temporaryChat,
+ defaultPerms[PermissionTypes.TEMPORARY_CHAT]?.[Permissions.USE],
+ defaults.temporaryChat,
+ ),
},
[PermissionTypes.RUN_CODE]: {
[Permissions.USE]: getPermissionValue(
diff --git a/packages/api/src/files/retention.spec.ts b/packages/api/src/files/retention.spec.ts
index 4722710a0c..bfd327508b 100644
--- a/packages/api/src/files/retention.spec.ts
+++ b/packages/api/src/files/retention.spec.ts
@@ -52,6 +52,16 @@ describe('retention helpers', () => {
expect(dependencies.getConvo).not.toHaveBeenCalled();
});
+ it('returns expiry when retentionMode is EPHEMERAL', async () => {
+ const result = await getRetentionExpiry(
+ request({ config: { interfaceConfig: { retentionMode: RetentionMode.EPHEMERAL } } }),
+ dependencies,
+ );
+
+ expect(result).toEqual({ expiredAt: expirationDate });
+ expect(dependencies.getConvo).not.toHaveBeenCalled();
+ });
+
it('returns a fresh expiry when the conversation has an active expiration', async () => {
dependencies.getConvo.mockResolvedValue({
expiredAt: new Date(Date.now() + 60 * 60 * 1000),
diff --git a/packages/api/src/files/retention.ts b/packages/api/src/files/retention.ts
index 884f56fb2e..f8a2ff12d7 100644
--- a/packages/api/src/files/retention.ts
+++ b/packages/api/src/files/retention.ts
@@ -1,4 +1,4 @@
-import { RetentionMode } from 'librechat-data-provider';
+import { isAllDataRetention } from 'librechat-data-provider';
import { createFallbackRetentionDate } from '@librechat/data-schemas';
import type { AppConfig } from '@librechat/data-schemas';
@@ -103,7 +103,7 @@ async function computeRetentionExpiry(
req: RetentionRequest | null | undefined,
dependencies: RetentionDependencies,
): Promise {
- if (req?.config?.interfaceConfig?.retentionMode === RetentionMode.ALL) {
+ if (isAllDataRetention(req?.config?.interfaceConfig?.retentionMode)) {
return createRetentionExpiry(req, dependencies);
}
@@ -179,7 +179,7 @@ const shouldRetainPersistentAgentFile = ({
const interfaceConfig = req?.config?.interfaceConfig;
return (
isPersistentAgentResourceUpload({ messageAttachment, toolResource }) &&
- (interfaceConfig?.retentionMode !== RetentionMode.ALL ||
+ (!isAllDataRetention(interfaceConfig?.retentionMode) ||
interfaceConfig?.retainAgentFiles === true)
);
};
@@ -218,7 +218,7 @@ export async function getSharedLinkExpiration(
return undefined;
}
- const isRetentionAll = req?.config?.interfaceConfig?.retentionMode === RetentionMode.ALL;
+ const isRetentionAll = isAllDataRetention(req?.config?.interfaceConfig?.retentionMode);
const convo = await dependencies.getConvo(userId, conversationId);
if (!convo) {
return undefined;
diff --git a/packages/data-provider/specs/config-schemas.spec.ts b/packages/data-provider/specs/config-schemas.spec.ts
index 236295483b..292ba65595 100644
--- a/packages/data-provider/specs/config-schemas.spec.ts
+++ b/packages/data-provider/specs/config-schemas.spec.ts
@@ -4,6 +4,8 @@ import {
azureEndpointSchema,
endpointSchema,
RetentionMode,
+ isAllDataRetention,
+ isForcedTemporaryRetention,
configSchema,
interfaceSchema,
fileStorageSchema,
@@ -1005,6 +1007,24 @@ describe('interfaceSchema', () => {
expect(result.defaultPinnedTools).toBeUndefined();
});
+
+ it('accepts the ephemeral retention mode', () => {
+ const result = interfaceSchema.parse({ retentionMode: RetentionMode.EPHEMERAL });
+ expect(result.retentionMode).toBe(RetentionMode.EPHEMERAL);
+ expect(RetentionMode.EPHEMERAL).toBe('ephemeral');
+ });
+
+ it('classifies ephemeral as forced-temporary, all-data retention', () => {
+ expect(isAllDataRetention(RetentionMode.EPHEMERAL)).toBe(true);
+ expect(isAllDataRetention(RetentionMode.ALL)).toBe(true);
+ expect(isAllDataRetention(RetentionMode.TEMPORARY)).toBe(false);
+ expect(isAllDataRetention(undefined)).toBe(false);
+
+ expect(isForcedTemporaryRetention(RetentionMode.EPHEMERAL)).toBe(true);
+ expect(isForcedTemporaryRetention(RetentionMode.ALL)).toBe(false);
+ expect(isForcedTemporaryRetention(RetentionMode.TEMPORARY)).toBe(false);
+ expect(isForcedTemporaryRetention(undefined)).toBe(false);
+ });
});
describe('summarizationTriggerSchema', () => {
diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts
index 2e0439cc0e..87370f5eb6 100644
--- a/packages/data-provider/src/config.ts
+++ b/packages/data-provider/src/config.ts
@@ -1148,8 +1148,17 @@ export type TMcpServersConfig = z.infer;
export enum RetentionMode {
ALL = 'all',
TEMPORARY = 'temporary',
+ EPHEMERAL = 'ephemeral',
}
+/** Retention modes that apply expiration deadlines to all data, not just user-marked temporary chats. */
+export const isAllDataRetention = (mode?: RetentionMode | null): boolean =>
+ mode === RetentionMode.ALL || mode === RetentionMode.EPHEMERAL;
+
+/** Whether the retention mode forces every conversation to be temporary, overriding the per-chat toggle. */
+export const isForcedTemporaryRetention = (mode?: RetentionMode | null): boolean =>
+ mode === RetentionMode.EPHEMERAL;
+
export const interfaceSchema = z
.object({
privacyPolicy: z
diff --git a/packages/data-schemas/src/methods/conversation.spec.ts b/packages/data-schemas/src/methods/conversation.spec.ts
index 1d9621cc0f..d47c170a19 100644
--- a/packages/data-schemas/src/methods/conversation.spec.ts
+++ b/packages/data-schemas/src/methods/conversation.spec.ts
@@ -654,6 +654,30 @@ describe('Conversation Operations', () => {
expect(result?.isTemporary).toBe(false);
});
+ it('should force temporary conversation and set expiredAt when retentionMode is EPHEMERAL even if isTemporary is false', async () => {
+ mockCtx.isTemporary = false;
+ mockCtx.interfaceConfig = {
+ temporaryChatRetention: 24,
+ retentionMode: RetentionMode.EPHEMERAL,
+ };
+ const result = await saveConvo(mockCtx, mockConversationData);
+ expect(result?.isTemporary).toBe(true);
+ expect(result?.expiredAt).toBeDefined();
+ expect(result?.expiredAt).not.toBeNull();
+ });
+
+ it('should force temporary conversation when retentionMode is EPHEMERAL and isTemporary is omitted', async () => {
+ mockCtx.isTemporary = undefined;
+ mockCtx.interfaceConfig = {
+ temporaryChatRetention: 24,
+ retentionMode: RetentionMode.EPHEMERAL,
+ };
+ const result = await saveConvo(mockCtx, mockConversationData);
+ expect(result?.isTemporary).toBe(true);
+ expect(result?.expiredAt).toBeDefined();
+ expect(result?.expiredAt).not.toBeNull();
+ });
+
it('should filter out temporary conversations in getConvosByCursor', async () => {
// Create some test conversations
const newNonTemporaryConvo = await Conversation.create({
diff --git a/packages/data-schemas/src/methods/conversation.ts b/packages/data-schemas/src/methods/conversation.ts
index cc2594fd26..c584a0b7f1 100644
--- a/packages/data-schemas/src/methods/conversation.ts
+++ b/packages/data-schemas/src/methods/conversation.ts
@@ -252,7 +252,16 @@ export function createConversationMethods(
update.conversationId = newConversationId;
}
- if (interfaceConfig?.retentionMode === RetentionMode.ALL) {
+ if (interfaceConfig?.retentionMode === RetentionMode.EPHEMERAL) {
+ update.isTemporary = true;
+ try {
+ update.expiredAt = createTempChatExpirationDate(interfaceConfig);
+ } catch (err) {
+ logger.error('Error creating temporary chat expiration date:', err);
+ logger.info(`---\`saveConvo\` context: ${metadata?.context}`);
+ update.expiredAt = createFallbackRetentionDate();
+ }
+ } else if (interfaceConfig?.retentionMode === RetentionMode.ALL) {
if (typeof isTemporary === 'boolean') {
update.isTemporary = isTemporary;
}
diff --git a/packages/data-schemas/src/methods/message.spec.ts b/packages/data-schemas/src/methods/message.spec.ts
index 7e3747d12c..89d6470712 100644
--- a/packages/data-schemas/src/methods/message.spec.ts
+++ b/packages/data-schemas/src/methods/message.spec.ts
@@ -608,6 +608,30 @@ describe('Message Operations', () => {
expect(result?.expiredAt).toBeNull();
});
+ it('should force temporary message and set expiredAt when retentionMode is EPHEMERAL even if isTemporary is false', async () => {
+ mockCtx.isTemporary = false;
+ mockCtx.interfaceConfig = {
+ temporaryChatRetention: 24,
+ retentionMode: RetentionMode.EPHEMERAL,
+ };
+ const result = await saveMessage(mockCtx, mockMessageData);
+ expect(result?.isTemporary).toBe(true);
+ expect(result?.expiredAt).toBeDefined();
+ expect(result?.expiredAt).toBeInstanceOf(Date);
+ });
+
+ it('should force temporary message when retentionMode is EPHEMERAL and isTemporary is omitted', async () => {
+ mockCtx.isTemporary = undefined;
+ mockCtx.interfaceConfig = {
+ temporaryChatRetention: 24,
+ retentionMode: RetentionMode.EPHEMERAL,
+ };
+ const result = await saveMessage(mockCtx, mockMessageData);
+ expect(result?.isTemporary).toBe(true);
+ expect(result?.expiredAt).toBeDefined();
+ expect(result?.expiredAt).toBeInstanceOf(Date);
+ });
+
it('should handle missing config gracefully', async () => {
// Simulate missing config - should use default retention period
delete mockCtx.interfaceConfig;
diff --git a/packages/data-schemas/src/methods/message.ts b/packages/data-schemas/src/methods/message.ts
index 42273cbf5f..89ad960095 100644
--- a/packages/data-schemas/src/methods/message.ts
+++ b/packages/data-schemas/src/methods/message.ts
@@ -119,7 +119,16 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
messageId: params.newMessageId || params.messageId,
};
- if (interfaceConfig?.retentionMode === RetentionMode.ALL) {
+ if (interfaceConfig?.retentionMode === RetentionMode.EPHEMERAL) {
+ update.isTemporary = true;
+ try {
+ update.expiredAt = createTempChatExpirationDate(interfaceConfig);
+ } catch (err) {
+ logger.error('Error creating temporary chat expiration date:', err);
+ logger.info(`---\`saveMessage\` context: ${metadata?.context}`);
+ update.expiredAt = createFallbackRetentionDate();
+ }
+ } else if (interfaceConfig?.retentionMode === RetentionMode.ALL) {
if (typeof isTemporary === 'boolean') {
update.isTemporary = isTemporary;
}