From 4a9fe9957d2dcccf1d703ff0356656ce7b741ef2 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 17 Jun 2026 02:39:36 -0700 Subject: [PATCH] FN-6504: allow attachment-only chat sends Allow chat composers and API routes to deliver attachment-only messages without placeholder text. - Permit main chat sends when staged attachments exist even if the textarea is blank. - Accept upload-backed and referenced attachment-only payloads in chat and room message routes while still rejecting fully empty messages. - Add UI/API regression coverage, documentation, and a patch changeset for attachment-only chat sends. Files changed: .changeset/fn-6504-attachment-only-send.md | 5 ++ docs/dashboard-guide.md | 1 + packages/dashboard/app/components/ChatView.tsx | 72 ++++++++++++++++++++-- .../app/components/__tests__/ChatView.test.tsx | 39 ++++++++++++ .../src/__tests__/chat-attachment-routes.test.ts | 24 +++++++- .../src/__tests__/chat-routes.rooms.test.ts | 62 +++++++++++++------ .../src/routes/register-chat-room-routes.ts | 13 +++- .../dashboard/src/routes/register-chat-routes.ts | 21 ++++--- 8 files changed, 205 insertions(+), 32 deletions(-) Fusion-Task-Id: FN-6504 Fusion-Task-Lineage: f64f7874-4e32-4ae5-a5b1-d8c4b78e23dd --- .changeset/fn-6504-attachment-only-send.md | 5 ++ docs/dashboard-guide.md | 1 + .../dashboard/app/components/ChatView.tsx | 72 +++++++++++++++++-- .../components/__tests__/ChatView.test.tsx | 39 ++++++++++ .../__tests__/chat-attachment-routes.test.ts | 24 ++++++- .../src/__tests__/chat-routes.rooms.test.ts | 62 +++++++++++----- .../src/routes/register-chat-room-routes.ts | 13 +++- .../src/routes/register-chat-routes.ts | 21 ++++-- 8 files changed, 205 insertions(+), 32 deletions(-) create mode 100644 .changeset/fn-6504-attachment-only-send.md diff --git a/.changeset/fn-6504-attachment-only-send.md b/.changeset/fn-6504-attachment-only-send.md new file mode 100644 index 0000000000..524e90e912 --- /dev/null +++ b/.changeset/fn-6504-attachment-only-send.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Allow chat attachments to be sent without accompanying text in Quick Chat and Main Chat while still rejecting fully empty sends. diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 23c14d8b14..4404c7c2c0 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -251,6 +251,7 @@ Chat view provides project-scoped conversations with agents. - The desktop Chat view toggle and mobile Chat tab now show an unread-response indicator when a live assistant reply arrives for your active chat thread after you leave Chat; opening Chat clears it immediately. - Agent-backed chat sessions now expose the same mailbox messaging tools (`fn_send_message`, `fn_read_messages`) used by runtime execution/heartbeat flows whenever the engine `MessageStore` is available; model-only chats continue to run without mailbox tools. - Chat attachments are included in agent-visible prompts for both direct sessions and rooms: supported text attachments are appended under an `Attachments` prompt section, and supported images (`png`, `jpeg`, `gif`, `webp`) are passed as image inputs to the model. +- Chat attachments can be sent without accompanying text in both Quick Chat and Main Chat; fully empty sends with no text and no attachments are still blocked. ![Chat view](./screenshots/chat-view.png) diff --git a/packages/dashboard/app/components/ChatView.tsx b/packages/dashboard/app/components/ChatView.tsx index cba6226ce7..70806838ba 100644 --- a/packages/dashboard/app/components/ChatView.tsx +++ b/packages/dashboard/app/components/ChatView.tsx @@ -2025,7 +2025,12 @@ export function ChatView({ projectId, addToast, experimentalFeatures }: ChatView const handleSendDispatch = useCallback(async () => { const trimmed = messageInput.trim(); - if (!trimmed) { + const files = pendingAttachments.map((attachment) => attachment.file); + /** + * FNXC:Chat 2026-06-17-02:12: + * Main Chat room dispatch must permit attachment-only sends. Block only a truly empty composer so staged files can reach the backend without requiring filler text. + */ + if (!trimmed && files.length === 0) { return; } @@ -2053,7 +2058,7 @@ export function ChatView({ projectId, addToast, experimentalFeatures }: ChatView clearComposerState(); try { - await rooms.sendRoomMessage(trimmed, { files: pendingAttachments.map((attachment) => attachment.file) }); + await rooms.sendRoomMessage(trimmed, { files }); } catch (error) { if (error instanceof RoomMessageDeliveredButReplyFailedError) { const message = error.message.trim() @@ -3635,8 +3640,66 @@ export function ChatView({ projectId, addToast, experimentalFeatures }: ChatView {rooms.activeRoom && (
+ { + handleAttachmentFiles(event.target.files); + event.target.value = ""; + }} + /> + {pendingAttachments.length > 0 && ( +
+ {pendingAttachments.map((attachment, index) => ( +
+ {attachment.previewUrl ? ( + {attachment.file.name} + ) : ( + {attachment.file.name} + )} + +
+ ))} +
+ )}
-
+ +
{ + event.preventDefault(); + setIsDragOver(true); + }} + onDragLeave={() => setIsDragOver(false)} + onDrop={(event) => { + event.preventDefault(); + setIsDragOver(false); + handleAttachmentFiles(event.dataTransfer.files); + }} + >