feat(chat): attachment UX, upload-as-file, and home/cloud submit fixes#712
feat(chat): attachment UX, upload-as-file, and home/cloud submit fixes#712FraterCCCLXIII wants to merge 19 commits into
Conversation
Combine the chat tools dropdown with the + control and add an Add Files and Images action with a paperclip icon at the bottom. Co-authored-by: Cursor <cursoragent@cursor.com>
…puts Read pasted screenshots from clipboard items, wire the home launcher through the shared attachment upload flow, and send first messages with attachments after creating a conversation. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the global checkbox with a circular upload button on clipboard pasted images, track paste source in the store, and fix overlay stacking. Co-authored-by: Cursor <cursoragent@cursor.com>
Match the remove button size and corner inset, show a checkmark when active with hover feedback, and swap the tooltip to "Do not upload as file". Co-authored-by: Cursor <cursoragent@cursor.com>
Match the chat input container's top inset so pasted images and files are spaced evenly above and below the thumbnail strip. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace invalid primary tokens with oh-surface/oh-muted styling for both states and raise the button slightly from the thumbnail corner. Co-authored-by: Cursor <cursoragent@cursor.com>
Cloud conversation creation can return a provisional `task-{uuid}` URL
while the sandbox provisions. Sending messages or uploading files against
that id caused 422 UUID parsing errors on the home `/conversations` input.
- Queue attachments in memory keyed by start-task id when provisioning
- Flush uploads and the user message once `useTaskPolling` sees READY
- Always include typed text in the start request, even with attachments
- Add store and flush helper tests
Paste/drag on the home input were already wired; this fixes starting a
conversation with attachments (or text + attachments) on cloud backends.
Co-authored-by: Cursor <cursoragent@cursor.com>
…eady" This reverts commit 9a44308.
…ready" This reverts commit 7ba6a26.
File uploads targeted read-only /workspace in Docker dev stacks and cloud task flush used raw i18next before app init. Resolve the conversation working_dir for upload paths and use the initialized openhands i18n instance when flushing deferred attachments. Co-authored-by: Cursor <cursoragent@cursor.com>
Cloud file uploads and the first message were hitting the bundled local agent-server and 404ing. Defer home attachments until the start task is ready, upload via the provisioned runtime URL, and send events through the cloud proxy. Co-authored-by: Cursor <cursoragent@cursor.com>
Skip initial_message when starting with attachments and avoid enqueueing optimistic duplicates after the attachment send already persisted the message. Co-authored-by: Cursor <cursoragent@cursor.com>
Enqueue optimistic pending messages on cloud start-task routes so the chat shows the user's message while the sandbox provisions, hide empty-state suggestions during provisioning, and reassign pending bubbles to the real conversation id when the task is ready. Co-authored-by: Cursor <cursoragent@cursor.com>
Skip auto-pagination when the initial history page is complete, on cloud start-task routes, or while provisioning, and stop surfacing an error banner when older events cannot be anchored. Co-authored-by: Cursor <cursoragent@cursor.com>
Treat optimistic home-submit bubbles as loaded content so the feed skeleton does not flash over the user's first message on new conversations. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep pending bubbles linked across task-to-conversation redirect, reassign them before paint on the real route, and tighten suggestion gating so the Let's Start Building overlay does not flash during provisioning. Co-authored-by: Cursor <cursoragent@cursor.com>
Mark every attached image for the per-image upload control, not just clipboard pastes, so file picker and drag-and-drop previews match pasted screenshot behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the generic upload glyph with Lucide FilePlus so the per-image control reads more clearly as "add this image as a workspace file." Co-authored-by: Cursor <cursoragent@cursor.com>
|
@FraterCCCLXIII is attempting to deploy a commit to the openhands Team on Vercel. A member of the Team first needs to authorize it. |
all-hands-bot
left a comment
There was a problem hiding this comment.
This PR significantly improves the attachment UX and fixes several cloud conversation start issues. However, there are critical race conditions in the new task/attachment coordination code that must be addressed before merging.
🔴 Critical Issues:
pending-task-message-link.tsline 29: SinglescheduledPendingReassignvariable will be overwritten by concurrent task completionsuse-task-polling.tslines 106-124: Async IIFE has no cancellation - will navigate after unmountpending-task-attachments-store.tslines 30-36: Non-atomic consume operation allows double-consumptionhome-chat-launcher.tsxlines 59-62:imagesMarkedUploadAsFilenot snapshotted - causes array index mismatches
🟠 Important Issues:
5. enqueue-home-task-pending-message.ts line 19: No error handling for image conversion failures
6. send-message-with-attachments.ts line 49: Promise.all fails completely if one image fails (should use Promise.allSettled)
7. use-load-older-events.ts line 156: setState after unmount (missing cancellation ref)
8. chat-interface.tsx lines 312, 327: Non-null assertions without guards
9. home-chat-launcher.tsx line 112: Passing null to displayErrorToast (poor UX)
🟡 Code quality:
10. conversation-file-upload.api.ts line 134: Redundant getSafeUploadFileName call
11. send-message-with-attachments.ts line 64: Empty content creates malformed prompt with leading newlines
See inline comments for the critical race conditions. The Important and Code quality issues should also be addressed but are less severe.
Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/agent-canvas/actions/runs/26241015556
| fromConversationId: string, | ||
| toConversationId: string, | ||
| ): void { | ||
| scheduledPendingReassign = { fromConversationId, toConversationId }; |
There was a problem hiding this comment.
🔴 Critical - Race Condition: The single scheduledPendingReassign variable will be overwritten if multiple cloud tasks complete concurrently. If a user starts two conversations from the home page rapidly, the second call to schedulePendingTaskMessageReassign() overwrites the first, causing the first task's pending messages to never be reassigned.
Fix: Replace with a Map keyed by toConversationId to handle multiple concurrent tasks.
| })), | ||
|
|
||
| consumePendingTaskAttachments: (taskId) => { | ||
| const payload = get().byTaskId[taskId]; |
There was a problem hiding this comment.
🔴 Critical - Race Condition: Non-atomic consume operation. The get() on line 31 and set() on line 36 are separate operations. If consumePendingTaskAttachments() is called twice rapidly with the same taskId, both calls could read the same payload before either removes it, violating consume-once semantics.
Fix: Make the read-and-remove atomic by doing both inside a single setState() callback, similar to consumeMatchingPendingMessage in optimistic-user-message-store.ts.
There was a problem hiding this comment.
Hello @FraterCCCLXIII,
I identified a few issues while testing the pull request locally.
Issue 1 (Blocking):
Step 1: Connect to the OpenHands Cloud Backend (https://app.all-hands.dev).
Step 2: Enter and send a message using the chat input on the home page.
Step 3: The sent message appears duplicated and remains in a pending state even after the agent has responded.
Please refer to the video below for additional context.
issue1.mov
Issue 2 (Blocking):
Step 1: Connect to the OpenHands Cloud Backend (https://app.all-hands.dev).
Step 2: Attach and send an image.
Step 3: The agent does not appear to recognize the attached image.
Please refer to the video below for additional context.
issue2.mov
Thank you very much! 🙏




Summary
This PR improves chat attachment UX and fixes several home-to-conversation launch bugs, especially on cloud backends.
Chat input & attachments
/workspacepaths in Docker dev stacks).Home & cloud conversation start
task-{uuid}→ real conversation id).Chat feed polish (new conversations)
Test plan
task-{uuid}, attachments flush after READYnpm testpassesMade with Cursor