fix: forward isTemporary from client for retention on file uploads and tool calls

Server-side `getRetentionExpiry` (file uploads) and the tool-call
controller both read `req.body.isTemporary`, but the file upload
multipart form and the tool-call payload did not include that field.
In `retentionMode: temporary` (default), files uploaded and tool
calls created from temporary chats were therefore retained
indefinitely.

Forward the Recoil `isTemporary` flag in both client paths so the
existing server checks can fire correctly. `ToolParams` gains an
optional `isTemporary` field.

Addresses Codex P1 review feedback on PR #29.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aron Gates 2026-05-08 10:38:22 -04:00
parent b881c52a9e
commit 7e937df05a
No known key found for this signature in database
GPG key ID: 4F5BDD01E0CFE2A0
3 changed files with 21 additions and 3 deletions

View file

@ -1,5 +1,6 @@
import React, { useState, useMemo, useCallback, useEffect, useRef } from 'react';
import debounce from 'lodash/debounce';
import { useRecoilValue } from 'recoil';
import { Tools } from 'librechat-data-provider';
import { TerminalSquareIcon, Check, X } from 'lucide-react';
import { Spinner, TooltipAnchor, useToastContext } from '@librechat/client';
@ -8,6 +9,7 @@ import { useToolCallMutation } from '~/data-provider';
import { useLocalize } from '~/hooks';
import { cn, normalizeLanguage } from '~/utils';
import { useMessageContext } from '~/Providers';
import store from '~/store';
type RunState = 'idle' | 'loading' | 'success' | 'error';
@ -23,6 +25,7 @@ const RunCode: React.FC<CodeBarProps & { iconOnly?: boolean }> = React.memo(
const { messageId, conversationId, partIndex } = useMessageContext();
const normalizedLang = useMemo(() => normalizeLanguage(lang), [lang]);
const isTemporary = useRecoilValue(store.isTemporary);
const handleExecute = useCallback(async () => {
const codeString: string = codeRef.current?.textContent ?? '';
@ -42,8 +45,18 @@ const RunCode: React.FC<CodeBarProps & { iconOnly?: boolean }> = React.memo(
conversationId: conversationId ?? '',
lang: normalizedLang,
code: codeString,
isTemporary,
});
}, [codeRef, execute, partIndex, messageId, blockIndex, conversationId, normalizedLang]);
}, [
codeRef,
execute,
partIndex,
messageId,
blockIndex,
conversationId,
normalizedLang,
isTemporary,
]);
const debouncedExecute = useMemo(
() => debounce(handleExecute, 1000, { leading: true }),

View file

@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useRef, useMemo, useState } from 'react';
import { v4 } from 'uuid';
import { useSetRecoilState } from 'recoil';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useToastContext } from '@librechat/client';
import { useQueryClient } from '@tanstack/react-query';
import {
@ -22,7 +22,7 @@ import useLocalize, { TranslationKeys } from '~/hooks/useLocalize';
import { useDelayedUploadToast } from './useDelayedUploadToast';
import { processFileForUpload } from '~/utils/heicConverter';
import { useChatContext } from '~/Providers/ChatContext';
import { ephemeralAgentByConvoId } from '~/store';
import store, { ephemeralAgentByConvoId } from '~/store';
import useClientResize from './useClientResize';
import useUpdateFiles from './useUpdateFiles';
@ -57,6 +57,7 @@ const useFileHandlingCore = (params: UseFileHandling | undefined, fileState: Fil
const setEphemeralAgent = useSetRecoilState(
ephemeralAgentByConvoId(conversation?.conversationId ?? Constants.NEW_CONVO),
);
const isTemporary = useRecoilValue(store.isTemporary);
const setError = (error: string) => setErrors((prevErrors) => [...prevErrors, error]);
const { addFile, replaceFile, updateFileById, deleteFileById } = useUpdateFiles(
params?.fileSetter ?? setFiles,
@ -192,6 +193,9 @@ const useFileHandlingCore = (params: UseFileHandling | undefined, fileState: Fil
formData.append('endpointType', endpointType ?? '');
formData.append('file', extendedFile.file as File, encodeURIComponent(filename));
formData.append('file_id', extendedFile.file_id);
if (isTemporary) {
formData.append('isTemporary', 'true');
}
const width = extendedFile.width ?? 0;
const height = extendedFile.height ?? 0;

View file

@ -446,6 +446,7 @@ export type ToolParams<T extends ToolId> = ToolParamsMap[T] & {
partIndex?: number;
blockIndex?: number;
conversationId: string;
isTemporary?: boolean;
};
export type ToolCallResponse = { result: unknown; attachments?: types.TAttachment[] };
export type ToolCallMutationOptions<T extends ToolId> = MutationOptions<