From 4d9cc89af4db4c91e0de80863d57d84bd17e7fe6 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 28 Apr 2026 23:31:16 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=B9=20chore:=20Tighten=20code-executio?= =?UTF-8?q?n=20attachment=20polish=20per=20audit=20feedback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves the eight actionable findings from the comprehensive audit: - Scope `displayFilename` out of `FileContainer`: opt-in via a new `displayName` prop. User-uploaded chips (input area, persisted message files) keep their raw filename, eliminating the false-positive class where `report-abc123.pdf` was silently rewritten to `report.pdf`. Code-execution artifact paths in `Attachment.tsx` explicitly compute the de-suffixed name and pass it through. - Tighten `TRAILING_NOTES_PATTERN` to anchor on the two known boilerplate openings (`Files from previous executions`, `Files in "Available files"`), so a user-authored `Note:` line preceded by a blank line in stdout no longer gets eaten along with everything after it. - `ToolMermaidArtifact`: compute `visibleFilename` once and reuse for title, content, and the download `aria-label` (was using the raw `attachment.filename` for the aria-label, creating a screen-reader inconsistency). - `ToolArtifactCard`: read `isSubmittingFamily(0)` once via a non-subscribing `useRecoilCallback`, instead of subscribing for the full lifetime to a value the ref only ever needs at first render. - Extract `bySalience` and `byEntrySalience` comparators from `attachmentTypes.ts`, replacing the ten duplicated sort lambdas in `Attachment.tsx` and `LogContent.tsx`. - Treat `attachmentSalience({ bytes: undefined })` as neutral (`0`) rather than empty (`1`); only an explicit `bytes === 0` sinks. Stops non-code-exec sources (web-search inline results, files where the schema omits the byte count) from silently sinking past real content. - Pin the click-history test to the panel-open button by name instead of relying on `getByRole('button', { pressed: false })`, which matched by DOM order. - Add the missing blank line between adjacent `it(...)` blocks. - Drop the verbose narrating comments in `FileContainer` along with the removed `displayFilename` import. Adds three regression tests for the new behavior (FileContainer raw filename, artifact-context displayName flow, user-authored `Note:` line preserved through cleanup) and updates the salience test for the new neutral-undefined semantics. --- .../Chat/Input/Files/FileContainer.tsx | 14 +++-- .../Files/__tests__/FileContainer.spec.tsx | 57 +++++++++++++++++++ .../Messages/Content/Parts/Attachment.tsx | 18 +++--- .../Messages/Content/Parts/LogContent.tsx | 13 +++-- .../Content/Parts/ToolArtifactCard.tsx | 32 ++++++++--- .../Content/Parts/ToolMermaidArtifact.tsx | 8 ++- .../Parts/__tests__/ArtifactRouting.test.tsx | 30 ++++++++-- .../Parts/__tests__/attachmentTypes.test.ts | 9 ++- .../Messages/Content/Parts/attachmentTypes.ts | 40 +++++++++---- packages/api/src/agents/cleanup.spec.ts | 22 +++++++ packages/api/src/agents/cleanup.ts | 11 +++- 11 files changed, 206 insertions(+), 48 deletions(-) create mode 100644 client/src/components/Chat/Input/Files/__tests__/FileContainer.spec.tsx diff --git a/client/src/components/Chat/Input/Files/FileContainer.tsx b/client/src/components/Chat/Input/Files/FileContainer.tsx index 4ade91584a..a4b902fac4 100644 --- a/client/src/components/Chat/Input/Files/FileContainer.tsx +++ b/client/src/components/Chat/Input/Files/FileContainer.tsx @@ -1,6 +1,5 @@ import type { TFile } from 'librechat-data-provider'; import type { ExtendedFile } from '~/common'; -import { displayFilename } from '~/components/Chat/Messages/Content/Parts/attachmentTypes'; import { getFileType, cn } from '~/utils'; import FilePreview from './FilePreview'; import RemoveFile from './RemoveFile'; @@ -8,6 +7,7 @@ import RemoveFile from './RemoveFile'; const FileContainer = ({ file, overrideType, + displayName, buttonClassName, containerClassName, onDelete, @@ -15,17 +15,19 @@ const FileContainer = ({ }: { file: Partial; overrideType?: string; + /** + * Optional pre-computed label for the chip. Callers in code-execution + * artifact contexts pass the de-suffixed name; upload chips and + * persisted user files leave this undefined and render the raw filename. + */ + displayName?: string; buttonClassName?: string; containerClassName?: string; onDelete?: () => void; onClick?: React.MouseEventHandler; }) => { const fileType = getFileType(overrideType ?? file.type); - // The on-disk filename can carry a `-<6 hex>` collision suffix that - // `sanitizeArtifactPath` adds when sanitization mutated the raw input - // (`.dirkeep` → `_.dirkeep-88b30b`). Show the canonical name in the - // chip; downloads still use `file.filename` so lookup is unaffected. - const visibleName = displayFilename(file.filename); + const visibleName = displayName ?? file.filename ?? ''; return (
({ + cn: (...classes: Array) => classes.filter(Boolean).join(' '), + getFileType: () => ({ paths: [], color: '', title: 'Plain' }), +})); + +jest.mock('../FilePreview', () => ({ + __esModule: true, + default: () =>
, +})); + +jest.mock('../RemoveFile', () => ({ + __esModule: true, + default: () =>