Skip to content

Add chat and ticket file uploads for Web & Widget (Mobile and RN-sdk TBC)#24

Merged
djanogly merged 6 commits intodevfrom
openspec/add-chat-and-ticket-file-uploads
Mar 12, 2026
Merged

Add chat and ticket file uploads for Web & Widget (Mobile and RN-sdk TBC)#24
djanogly merged 6 commits intodevfrom
openspec/add-chat-and-ticket-file-uploads

Conversation

@djanogly
Copy link
Contributor

This pull request adds support for file attachments in the inbox conversation reply UI. It introduces the ability to upload, display, and remove attachments when composing a reply, and updates the message display to show attachments. The changes span the UI components, test coverage, and hooks for managing attachment state and backend integration.

Attachment Support in Inbox UI:

  • Added props and UI logic in InboxThreadPane to support pending attachments, attachment uploading state, and new callbacks for uploading/removing attachments. Attachments are now displayed both for sent messages and pending replies, with file size formatting and removal capability. The reply input and send button are disabled appropriately during upload. [1] [2] [3] [4] [5] [6]

Test Coverage:

  • Added a test to verify rendering of both sent message attachments and queued reply attachments, including removal interaction.

Inbox Message Actions and State Management:

  • Updated the message actions hook to manage pendingAttachments in state, include attachment IDs in message sending, and improve optimistic UI for attachment-only replies. [1] [2] [3]

Backend Integration for Attachments:

  • Added Convex mutation references and types for generating upload URLs and finalizing support attachment uploads, and exposed them in the inbox hooks API. [1] [2] [3] [4]

Miscellaneous:

  • Updated imports, commented out unused snippet-related code, and made minor refactoring for clarity. [1] [2]

These changes collectively enable a seamless file attachment experience in the inbox reply workflow, with full state management and test coverage.

@djanogly djanogly requested a review from Copilot March 12, 2026 14:36
@vercel
Copy link

vercel bot commented Mar 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
opencom-landing Ready Ready Preview, Comment Mar 12, 2026 5:50pm
opencom-web Ready Ready Preview, Comment Mar 12, 2026 5:50pm

@qodo-code-review
Copy link

Review Summary by Qodo

Add file attachment support for chat and ticket workflows across web, widget, and backend

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Comprehensive file attachment support for chat and ticket workflows across web, widget, and
  backend systems
• New supportAttachments backend module with mutations for generating upload URLs, finalizing
  uploads, and managing attachment lifecycle with TTL-based cleanup
• Shared attachment types, validators, and client-side upload orchestration utilities in
  web-shared package
• Database schema additions for storing attachment references in messages, tickets, and comments
  with proper indexing
• Inbox conversation UI enhancements with attachment input, pending attachment display, and removal
  capability
• Ticket detail and creation UI updates with attachment upload support and download links for sent
  attachments
• Widget conversation and ticket components updated with full attachment workflow integration
• Comprehensive test coverage including attachment upload validation, lifecycle management,
  authorization checks, and UI rendering
• Test data cleanup utilities and E2E test helpers for attachment management
Diagram
flowchart LR
  Client["Client Apps<br/>Web/Widget"]
  Upload["Upload Handler<br/>Generate URL"]
  Storage["File Storage"]
  Finalize["Finalize Upload<br/>Bind to Message/Ticket"]
  DB["Database<br/>Messages/Tickets/Comments"]
  Display["Display Attachments<br/>with Download Links"]
  
  Client -->|"Select Files"| Upload
  Upload -->|"Get Upload URL"| Storage
  Client -->|"Upload File"| Storage
  Client -->|"Finalize Upload"| Finalize
  Finalize -->|"Store Attachment ID"| DB
  DB -->|"Fetch with Attachments"| Display
  Display -->|"Render Links"| Client
Loading

Grey Divider

File Changes

1. packages/convex/convex/supportAttachments.ts ✨ Enhancement +408/-0

Core support attachment upload and lifecycle management

• New file implementing core attachment upload and management functionality with mutations for
 generating upload URLs and finalizing uploads
• Includes staged attachment cleanup with TTL-based expiration and internal mutation scheduling
• Provides helper functions for binding staged attachments to messages, tickets, and comments with
 authorization checks
• Exports descriptor loading and materialization utilities for retrieving attachment metadata and
 URLs

packages/convex/convex/supportAttachments.ts


2. packages/convex/tests/supportAttachments.test.ts 🧪 Tests +333/-0

Support attachment upload and lifecycle test coverage

• New comprehensive test suite covering attachment upload workflows for both agents and visitors
• Tests file type validation, size limits, and rejection of unsupported uploads with cleanup
• Verifies attachment binding to messages, tickets, and comments with proper authorization
• Tests expired staged upload cleanup and underlying storage deletion

packages/convex/tests/supportAttachments.test.ts


3. apps/widget/src/hooks/useWidgetTicketFlow.ts ✨ Enhancement +129/-3

Widget ticket flow attachment upload integration

• Added state management for ticket creation and comment attachments with upload tracking
• Integrated attachment upload functionality using shared uploadSupportAttachments helper
• Updated ticket creation and comment mutations to include attachment IDs
• Added callbacks for removing attachments and uploading new ones

apps/widget/src/hooks/useWidgetTicketFlow.ts


View more (38)
4. packages/convex/convex/tickets.ts ✨ Enhancement +79/-12

Ticket system attachment binding and enrichment

• Added support for binding attachments to ticket creation and comment mutations
• Implemented withSupportAttachments helper to enrich ticket and comment records with attachment
 descriptors
• Updated all ticket queries to include attachment data with URLs and metadata
• Added attachment ID validation and actor determination for authorization

packages/convex/convex/tickets.ts


5. packages/convex/convex/messages.ts ✨ Enhancement +58/-4

Message system attachment binding and enrichment

• Added attachment support to message sending with binding to conversations
• Implemented withMessageAttachments helper to enrich messages with attachment descriptors
• Updated message list queries to include attachment data
• Modified message preview to show attachment count when content is empty

packages/convex/convex/messages.ts


6. packages/web-shared/src/supportAttachments.ts ✨ Enhancement +200/-0

Shared attachment types and upload utilities

• New file with shared types for attachment descriptors, staged uploads, and finalization results
• Provides MIME type inference and validation utilities for supported file types
• Implements uploadSupportAttachments function for client-side upload orchestration
• Exports formatting and validation helpers for file size and attachment constraints

packages/web-shared/src/supportAttachments.ts


7. apps/web/src/app/inbox/hooks/useInboxMessageActions.ts ✨ Enhancement +37/-3

Inbox message actions attachment state management

• Added pendingAttachments state management to track staged attachments during composition
• Updated send message logic to include attachment IDs and handle attachment-only messages
• Implemented optimistic UI for attachment count display in message preview
• Added attachment state reset after successful message send

apps/web/src/app/inbox/hooks/useInboxMessageActions.ts


8. apps/web/src/app/tickets/hooks/useTicketsConvex.ts ✨ Enhancement +26/-0

Tickets Convex hooks attachment mutation integration

• Added mutation references for generating upload URLs and finalizing attachment uploads
• Updated ticket creation and comment mutations to accept attachment IDs
• Exposed attachment upload functions in both ticket list and detail hooks
• Added attachment descriptor types to ticket and comment query results

apps/web/src/app/tickets/hooks/useTicketsConvex.ts


9. packages/convex/convex/_generated/api.d.ts ⚙️ Configuration changes +8/-0

Generated API type definitions for attachment support

• Added imports for new supportAttachmentTables schema and supportAttachments module
• Added imports for supportAttachmentTypes module with validators and constants
• Added imports for testing_helpers_supportAttachments test utilities
• Updated API type definitions to include new attachment-related modules

packages/convex/convex/_generated/api.d.ts


10. apps/widget/src/hooks/convex/useConversationViewConvex.ts ✨ Enhancement +29/-0

Widget conversation view attachment upload integration

• Added mutation references for generating upload URLs and finalizing attachment uploads
• Exposed attachment upload functions in conversation view Convex hook
• Updated send message mutation type to include optional attachment IDs

apps/widget/src/hooks/convex/useConversationViewConvex.ts


11. packages/convex/convex/testData/cleanup.ts 🧪 Tests +34/-1

Test data cleanup for support attachments

• Added helper functions to delete support attachments and their underlying storage files
• Updated test data cleanup to remove attachments from tickets, comments, and messages
• Implemented recursive cleanup of attachment storage to prevent orphaned files

packages/convex/convex/testData/cleanup.ts


12. apps/web/src/app/inbox/hooks/useInboxConvex.ts ✨ Enhancement +23/-0

Inbox Convex hooks attachment mutation integration

• Added mutation references for generating upload URLs and finalizing attachment uploads
• Exposed attachment upload functions in inbox Convex hook
• Updated send message mutation type to include optional attachment IDs

apps/web/src/app/inbox/hooks/useInboxConvex.ts


13. packages/convex/convex/testing/helpers/supportAttachments.ts 🧪 Tests +74/-0

Support attachment testing helper mutations

• New file with internal mutations for testing attachment lifecycle
• Provides helpers to retrieve, expire, and cleanup test attachments
• Includes utility to check if stored files exist in storage

packages/convex/convex/testing/helpers/supportAttachments.ts


14. packages/convex/convex/testing/helpers/cleanup.ts 🧪 Tests +23/-1

Workspace-level attachment cleanup for testing

• Added deleteSupportAttachmentsForWorkspace function to clean up all workspace attachments
• Integrated attachment cleanup into test data cleanup and E2E test cleanup flows
• Ensures storage files are deleted along with database records

packages/convex/convex/testing/helpers/cleanup.ts


15. packages/convex/tests/runtimeTypeHardeningGuard.test.ts 🧪 Tests +12/-0

Runtime type hardening guard for attachment cleanup

• Added test to verify supportAttachments.ts uses fixed typed refs for cleanup scheduling
• Validates that CLEANUP_EXPIRED_STAGED_UPLOADS_REF and getShallowRunAfter are used
• Ensures no dynamic ref generation via getInternalRef function

packages/convex/tests/runtimeTypeHardeningGuard.test.ts


16. packages/convex/convex/testing/helpers.ts ⚙️ Configuration changes +5/-0

Test helpers module exports for attachments

• Added exports for support attachment test helpers from new supportAttachments module
• Exposed cleanupExpiredSupportAttachments, expireTestSupportAttachment,
 getTestSupportAttachment, and hasTestStoredFile mutations

packages/convex/convex/testing/helpers.ts


17. packages/convex/convex/schema/outboundSupportTables.ts ⚙️ Configuration changes +3/-0

Ticket and comment schema attachment field additions

• Added attachmentIds field to tickets table for storing attachment references
• Added attachmentIds field to ticket comments table for storing attachment references
• Imported supportAttachmentIdArrayValidator for field validation

packages/convex/convex/schema/outboundSupportTables.ts


18. packages/convex/convex/schema/supportAttachmentTables.ts ⚙️ Configuration changes +30/-0

Support attachments database table schema

• New file defining supportAttachments table schema with fields for file metadata and status
• Includes indexes for querying by workspace, message, ticket, comment, and expiration
• Tracks upload source (agent/visitor), binding status, and TTL expiration

packages/convex/convex/schema/supportAttachmentTables.ts


19. packages/convex/convex/supportAttachmentTypes.ts ⚙️ Configuration changes +33/-0

Support attachment types and validators

• New file with validators for attachment status and uploader types
• Defines constants for file size limits, attachment count limits, and TTL duration
• Specifies supported MIME types and user-friendly type label for validation messages

packages/convex/convex/supportAttachmentTypes.ts


20. apps/web/src/app/inbox/inboxRenderTypes.ts ⚙️ Configuration changes +2/-0

Inbox message type attachment field addition

• Added attachments field to InboxMessage type for displaying attachment descriptors
• Imported SupportAttachmentDescriptor type from shared utilities

apps/web/src/app/inbox/inboxRenderTypes.ts


21. packages/convex/convex/schema/inboxConversationTables.ts ⚙️ Configuration changes +2/-0

Message schema attachment field addition

• Added attachmentIds field to messages table for storing attachment references
• Imported supportAttachmentIdArrayValidator for field validation

packages/convex/convex/schema/inboxConversationTables.ts


22. packages/web-shared/src/index.ts ⚙️ Configuration changes +11/-0

Web-shared module exports for attachments

• Exported attachment-related types and utilities from new supportAttachments module
• Includes types for descriptors, staged uploads, and finalization results
• Exports validation, formatting, and upload helper functions

packages/web-shared/src/index.ts


23. packages/convex/convex/schema.ts ⚙️ Configuration changes +2/-0

Schema integration of attachment tables

• Added import and inclusion of supportAttachmentTables in schema definition

packages/convex/convex/schema.ts


24. apps/web/e2e/widget.spec.ts 🧪 Tests +2/-1

Widget E2E test skip for email capture

• Skipped email capture dismiss button test due to hidden UI element

apps/web/e2e/widget.spec.ts


25. apps/widget/src/components/conversationView/types.ts ⚙️ Configuration changes +2/-0

Conversation message type attachment field addition

• Added attachments field to ConversationMessage type for displaying attachment descriptors
• Imported SupportAttachmentDescriptor type from shared utilities

apps/widget/src/components/conversationView/types.ts


26. apps/widget/src/styles.css Formatting +189/-0

Widget styles for attachment UI components

• Added styles for attachment input container with flex column layout
• Implemented styles for pending attachments display with scrolling and removal buttons
• Added styles for message attachment display with links and file size formatting
• Updated composer row layout to accommodate attachments above input controls

apps/widget/src/styles.css


27. apps/web/src/app/inbox/InboxThreadPane.tsx ✨ Enhancement +135/-22

Inbox thread pane attachment UI implementation

• Added attachment input handling with file selection and upload triggering
• Implemented pending attachments display with removal capability
• Added attachment rendering for sent messages with download links and file sizes
• Updated send button and input disabled states to account for upload progress
• Disabled snippet save/update features (commented out)

apps/web/src/app/inbox/InboxThreadPane.tsx


28. apps/web/src/app/tickets/[id]/page.tsx ✨ Enhancement +170/-5

Ticket detail page attachment UI implementation

• Added attachment upload state management and error handling
• Implemented attachment input with file selection and upload triggering
• Added pending attachments display with removal capability
• Implemented attachment rendering for ticket descriptions and comments with download links
• Updated comment submission to include attachment IDs and handle upload states

apps/web/src/app/tickets/[id]/page.tsx


29. apps/widget/src/components/TicketDetail.tsx ✨ Enhancement +137/-8

Widget ticket detail attachment UI implementation

• Added attachment input handling with file selection and upload triggering
• Implemented pending attachments display with removal capability
• Added attachment rendering for ticket descriptions and comments with download links
• Updated comment submission to include attachment IDs and handle upload states
• Added error feedback display for attachment and comment operations

apps/widget/src/components/TicketDetail.tsx


30. apps/widget/src/components/conversationView/Footer.test.tsx 🧪 Tests +76/-0

Widget conversation footer attachment layout test

• New test file verifying composer layout with pending attachments
• Tests that attachment list and input controls remain properly organized
• Validates that pending attachments display separately from composer row

apps/widget/src/components/conversationView/Footer.test.tsx


31. apps/web/src/app/tickets/page.tsx ✨ Enhancement +152/-11

Add file attachment support to ticket creation

• Added file attachment upload support to ticket creation modal with pendingAttachments state
 management and upload handlers
• Integrated uploadSupportAttachments utility to handle file uploads with progress tracking and
 error handling
• Added attachment display UI showing file names, sizes, and removal capability in the create ticket
 form
• Implemented resetCreateTicketState helper to clear form and attachment state when closing modal

apps/web/src/app/tickets/page.tsx


32. apps/web/src/app/inbox/page.tsx ✨ Enhancement +70/-1

Add attachment upload state management to inbox

• Added pendingAttachments state and isUploadingAttachments flag to manage attachment uploads in
 inbox conversations
• Implemented handleUploadAttachments callback to upload files and handleRemovePendingAttachment
 to remove queued attachments
• Integrated attachment state into InboxThreadPane component props and message sending logic
• Added automatic clearing of pending attachments when switching between conversations

apps/web/src/app/inbox/page.tsx


33. apps/widget/src/components/ConversationView.tsx ✨ Enhancement +70/-2

Add attachment upload support to widget conversations

• Added pendingAttachments state and isUploadingAttachments flag for managing file uploads in
 widget conversations
• Implemented handleUploadAttachments async function to upload files with error handling and user
 feedback
• Updated sendMessage to include attachment IDs in message payload and clear attachments after
 successful send
• Modified message input validation to allow sending attachment-only messages without text content

apps/widget/src/components/ConversationView.tsx


34. apps/web/src/app/inbox/InboxThreadPane.test.tsx 🧪 Tests +60/-8

Update inbox thread pane tests for attachments

• Updated test props to include pendingAttachments, isUploadingAttachments, and new attachment
 callback handlers
• Modified existing test expectations to reflect removal of inline snippet workflow controls from
 composer
• Added comprehensive test case for rendering message attachments and pending reply attachments with
 removal interaction

apps/web/src/app/inbox/InboxThreadPane.test.tsx


35. apps/widget/src/components/conversationView/Footer.tsx ✨ Enhancement +87/-20

Add attachment UI to widget conversation footer

• Added hidden file input for attachment selection with SUPPORT_ATTACHMENT_ACCEPT filtering
• Implemented pending attachments display section showing file names, sizes, and removal buttons
• Added attachment button to composer row and updated send button to allow sending with attachments
 only
• Integrated error display for upload failures and disabled inputs during upload operations

apps/widget/src/components/conversationView/Footer.tsx


36. apps/widget/src/components/TicketCreate.tsx ✨ Enhancement +66/-4

Add attachment upload UI to widget ticket creation

• Added file input for attachment selection with support for multiple files and type filtering
• Implemented pending attachments display with file names, sizes, and removal capability
• Added attachment upload button and integrated upload state management into form submission logic
• Disabled form submission and input during active uploads

apps/widget/src/components/TicketCreate.tsx


37. openspec/changes/add-chat-and-ticket-file-uploads/tasks.md 📝 Documentation +12/-12

Mark all attachment feature tasks as complete

• Marked all 12 task items as completed across 4 major sections (Shared Attachment Domain, Chat
 Flows, Ticket Flows, Verification)
• Updated checkboxes from unchecked to checked status for all implementation and testing tasks

openspec/changes/add-chat-and-ticket-file-uploads/tasks.md


38. apps/widget/src/components/TicketDetail.test.tsx 🧪 Tests +72/-0

Add ticket detail attachment rendering tests

• Added new test file for TicketDetail component attachment functionality
• Tests rendering of ticket attachments, comment attachments, and pending reply attachments
• Verifies removal interaction for queued reply files with proper callback invocation

apps/widget/src/components/TicketDetail.test.tsx


39. apps/widget/src/components/conversationView/MessageList.tsx ✨ Enhancement +31/-8

Add attachment rendering to widget message list

• Added conditional rendering of message content only when text is present
• Implemented attachment display section showing downloadable attachment links with file names and
 sizes
• Imported formatSupportAttachmentSize utility and Paperclip icon for attachment rendering

apps/widget/src/components/conversationView/MessageList.tsx


40. apps/widget/src/Widget.tsx ✨ Enhancement +16/-0

Wire attachment state to ticket components

• Extracted attachment state and handlers from useWidgetTicketFlow hook including
 commentAttachments, createTicketAttachments, and isUploadingAttachments
• Passed attachment props and callbacks to both TicketCreate and TicketDetail components
• Integrated upload and removal handlers for both ticket creation and comment workflows

apps/widget/src/Widget.tsx


41. apps/widget/src/icons.tsx ✨ Enhancement +18/-0

Add paperclip icon for attachments

• Added new Paperclip SVG icon component for attachment UI elements
• Icon uses standard 20x20 viewBox with stroke styling matching existing icon patterns

apps/widget/src/icons.tsx


Grey Divider

Qodo Logo

@qodo-code-review
Copy link

qodo-code-review bot commented Mar 12, 2026

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. CLEANUP_EXPIRED... uses makeFunctionReference📘 Rule violation ⛯ Reliability
Description
Backend scheduling creates a cross-function reference via
makeFunctionReference("supportAttachments:cleanupExpiredStagedUploads") without any TS2589 hotspot
justification. This violates the requirement to use generated api/internal references for
cross-function calls.
Code

packages/convex/convex/supportAttachments.ts[R55-62]

+const CLEANUP_EXPIRED_STAGED_UPLOADS_REF = makeFunctionReference<
+  "mutation",
+  CleanupExpiredStagedUploadsArgs,
+  CleanupExpiredStagedUploadsResult
+>("supportAttachments:cleanupExpiredStagedUploads") as unknown as InternalMutationRef<
+  CleanupExpiredStagedUploadsArgs,
+  CleanupExpiredStagedUploadsResult
+>;
Evidence
PR Compliance ID 72008 requires using generated api/internal refs for backend cross-function
calls, only allowing makeFunctionReference("module:function") when explicitly documented as a
TS2589 hotspot. The new code introduces a manual string reference without any TS2589 comment/ticket
reference.

Rule 72008: Prefer generated api/internal references for backend cross-function calls
packages/convex/convex/supportAttachments.ts[55-62]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A backend cross-function call reference is being created via `makeFunctionReference(&amp;amp;quot;supportAttachments:cleanupExpiredStagedUploads&amp;amp;quot;)` without a TS2589 hotspot justification. Policy requires using generated `api`/`internal` references for cross-function calls.
## Issue Context
This reference is used for scheduling the internal mutation, but it is currently string-based.
## Fix Focus Areas
- packages/convex/convex/supportAttachments.ts[55-62]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Type-escape casts in supportAttachments.ts📘 Rule violation ✓ Correctness
Description
The new backend module introduces as unknown as type-escape casts inline, rather than isolating
them in an explicit adapter/hotspot helper. This spreads unsafe typing into runtime/business logic
and violates the localization requirement for type escapes.
Code

packages/convex/convex/supportAttachments.ts[R55-72]

+const CLEANUP_EXPIRED_STAGED_UPLOADS_REF = makeFunctionReference<
+  "mutation",
+  CleanupExpiredStagedUploadsArgs,
+  CleanupExpiredStagedUploadsResult
+>("supportAttachments:cleanupExpiredStagedUploads") as unknown as InternalMutationRef<
+  CleanupExpiredStagedUploadsArgs,
+  CleanupExpiredStagedUploadsResult
+>;
+
+function getShallowRunAfter(ctx: { scheduler: { runAfter: unknown } }) {
+  return ctx.scheduler.runAfter as unknown as <
+    Args extends Record<string, unknown>,
+    Return = unknown,
+  >(
+    delayMs: number,
+    functionRef: InternalMutationRef<Args, Return>,
+    runArgs: Args
+  ) => Promise<unknown>;
Evidence
PR Compliance ID 72009 requires that new as unknown as escape hatches live only in clearly named
adapters/bridges or explicitly documented hotspot helpers. The added casts appear directly in
packages/convex/convex/supportAttachments.ts with no hotspot/helper encapsulation or
documentation.

Rule 72009: Localize type-escape casts to adapters or explicit hotspot helpers
packages/convex/convex/supportAttachments.ts[55-62]
packages/convex/convex/supportAttachments.ts[64-72]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`as unknown as` type-escape casts were introduced directly inside `supportAttachments.ts`. Compliance requires localizing such unsafe boundaries to explicitly named and documented adapter/hotspot helper modules.
## Issue Context
The new code casts both the `makeFunctionReference(...)` result and `ctx.scheduler.runAfter` to broader/alternate types.
## Fix Focus Areas
- packages/convex/convex/supportAttachments.ts[55-72]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Upload crosses conversations🐞 Bug ✓ Correctness
Description
InboxContent.handleUploadAttachments appends uploaded attachments into a single
pendingAttachments state after awaiting the upload, even if selectedConversationId changed in
the meantime. This can surface (and send) files in the wrong conversation composer.
Code

apps/web/src/app/inbox/page.tsx[R463-480]

+  const handleUploadAttachments = useCallback(
+    async (files: File[]) => {
+      if (!activeWorkspace?._id || !selectedConversationId || files.length === 0) {
+        return;
+      }
+
+      setWorkflowError(null);
+      setIsUploadingAttachments(true);
+      try {
+        const uploadedAttachments = await uploadSupportAttachments({
+          files,
+          currentCount: pendingAttachments.length,
+          workspaceId: activeWorkspace._id,
+          generateUploadUrl: generateSupportAttachmentUploadUrl,
+          finalizeUpload: finalizeSupportAttachmentUpload,
+        });
+        setPendingAttachments((current) => [...current, ...uploadedAttachments]);
+      } catch (error) {
Evidence
pendingAttachments is cleared whenever the selected conversation changes, but the async upload
completion always appends to the current state without verifying the conversation is still the one
that initiated the upload, enabling cross-thread leakage when navigating mid-upload.

apps/web/src/app/inbox/page.tsx[234-236]
apps/web/src/app/inbox/page.tsx[463-480]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`handleUploadAttachments` writes results into a shared `pendingAttachments` state after an awaited upload, even if the user navigated to a different conversation while the upload was in-flight.
### Issue Context
This can cause attachments to appear in (and be sent from) the wrong conversation.
### Fix Focus Areas
- Add an &amp;amp;quot;upload context&amp;amp;quot; guard (e.g., capture `selectedConversationId` at upload start into a local const or ref; after `await`, only append if it still matches current selection).
- Consider clearing/invalidating in-flight uploads when `selectedConversationId` changes.
- apps/web/src/app/inbox/page.tsx[234-236]
- apps/web/src/app/inbox/page.tsx[463-490]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Optimistic preview clock skew🐞 Bug ⛯ Reliability
Description
Inbox optimistic-preview clearing compares server message createdAt with a client
Date.now()-derived patch.lastMessageAt. If the client clock is ahead of the server clock, the
optimistic preview can fail to clear and remain stale in the conversation list.
Code

apps/web/src/app/inbox/page.tsx[R248-251]

+      if (
+        latestMessage.senderType !== "agent" ||
+        latestMessage.createdAt < (patch.lastMessageAt ?? 0)
+      ) {
Evidence
patch.lastMessageAt is set using the browser clock when sending, but the messages’ createdAt
timestamps are generated on the server; the new clearing condition uses `latestMessage.createdAt <
patch.lastMessageAt`, which is sensitive to client/server clock skew.

apps/web/src/app/inbox/page.tsx[248-252]
apps/web/src/app/inbox/hooks/useInboxMessageActions.ts[140-149]
packages/convex/convex/messages.ts[193-201]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The optimistic preview clear logic uses a comparison between `latestMessage.createdAt` (server clock) and `patch.lastMessageAt` (client clock). With clock skew, the optimistic preview can stick.
### Issue Context
This affects inbox list freshness/ordering and is most visible for attachment-only messages (where content comparison was removed).
### Fix Focus Areas
- Replace the time-based comparison with correlation that doesn’t depend on client/server clock alignment (e.g., clear when messages list updates to include a newer agent message, or have `messages:send` return `{messageId, createdAt}` and track that).
- apps/web/src/app/inbox/page.tsx[238-272]
- apps/web/src/app/inbox/hooks/useInboxMessageActions.ts[136-159]
- packages/convex/convex/messages.ts[193-225]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. MIME type spoofing🐞 Bug ⛨ Security
Description
supportAttachments.finalizeUpload accepts/rejects uploads based on metadata.contentType, which
ultimately comes from the upload request’s Content-Type header. A malicious client can upload
arbitrary bytes while claiming an allowed content type, bypassing true file-type enforcement.
Code

packages/convex/convex/supportAttachments.ts[R332-339]

+    const mimeType = metadata.contentType?.trim() ?? "";
+    if (!SUPPORTED_SUPPORT_ATTACHMENT_MIME_TYPES.has(mimeType)) {
+      await deleteUploadedFileIfPresent(ctx, args.storageId);
+      return {
+        status: "rejected" as const,
+        message: `Unsupported file type. Allowed: ${SUPPORTED_SUPPORT_ATTACHMENT_TYPE_LABEL}.`,
+      };
+    }
Evidence
The client explicitly sets Content-Type for the upload, and the server validates only the
resulting stored metadata contentType against an allowlist; there is no server-side
signature/extension verification beyond this header-derived value.

packages/web-shared/src/supportAttachments.ts[161-176]
packages/convex/convex/supportAttachments.ts[332-339]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The backend currently trusts `metadata.contentType` for allowlisting, but that value is derived from the upload request header and can be spoofed.
### Issue Context
This is defense-in-depth hardening: the system should not treat client-provided `Content-Type` as authoritative.
### Fix Focus Areas
- Add extension-based validation (from normalized `fileName`) and require it to be in the allowlist.
- If feasible, add minimal signature checks for common formats (PNG/JPEG/PDF/ZIP) before accepting.
- packages/convex/convex/supportAttachments.ts[327-360]
- packages/web-shared/src/supportAttachments.ts[161-176]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Attachment URL not guarded🐞 Bug ⛯ Reliability
Description
Several UIs render attachment links with href={attachment.url} even though url is optional and
can be undefined when storage.getUrl returns null. This can render non-functional links without
fallback behavior.
Code

apps/widget/src/components/conversationView/MessageList.tsx[R92-110]

+                {msg.attachments && msg.attachments.length > 0 && (
+                  <div className="opencom-message-attachments">
+                    {msg.attachments.map((attachment) => (
+                      <a
+                        key={attachment._id}
+                        href={attachment.url}
+                        target="_blank"
+                        rel="noreferrer"
+                        className="opencom-message-attachment"
+                      >
+                        <span className="opencom-message-attachment-name">
+                          <Paperclip />
+                          {attachment.fileName}
+                        </span>
+                        <span className="opencom-message-attachment-size">
+                          {formatSupportAttachmentSize(attachment.size)}
+                        </span>
+                      </a>
+                    ))}
Evidence
The backend explicitly sets url to undefined when it cannot be retrieved, but the widget message
list (and ticket detail UI) always renders an anchor tag using attachment.url without checking it
exists.

packages/convex/convex/supportAttachments.ts[271-276]
apps/widget/src/components/conversationView/MessageList.tsx[92-110]
apps/web/src/app/tickets/[id]/page.tsx[299-306]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
UI components assume `attachment.url` always exists and render `&amp;amp;lt;a href={attachment.url}&amp;amp;gt;`, but backend can return `url: undefined`.
### Issue Context
This results in broken/non-clickable attachment entries without an explicit fallback.
### Fix Focus Areas
- Guard `attachment.url` before rendering anchor tags; render a non-link fallback if missing.
- Apply consistently across widget message list and ticket attachment renderers.
- apps/widget/src/components/conversationView/MessageList.tsx[92-112]
- apps/web/src/app/tickets/[id]/page.tsx[297-316]
- packages/convex/convex/supportAttachments.ts[253-276]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@qodo-code-review
Copy link

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: e2e

Failed stage: Playwright E2E suite [❌]

Failed test name: apps/web/e2e/ai-agent-settings.spec.ts:59:7 › Web Admin - AI Agent Settings › should display AI Agent section on settings page

Failure summary:

  • The GitHub Action failed during Playwright E2E execution (pnpm playwright test) because the
    Playwright Chromium browser binary was not installed on the runner.
  • Playwright reports for many tests: browserType.launch: Executable doesn't exist at
    /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/.../chrome-headless-shell and
    instructs to run pnpm exec playwright install.
  • Because the browser could not launch, 137 E2E tests failed (not functional test assertions), causing
    the web:test:e2e script to exit with code 1 (ELIFECYCLE).
  • The subsequent reliability gate failed as a consequence: [e2e-reliability-gate] unexpected exceeded
    threshold: observed=137 budget=0 allowance=0 threshold=0.
Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

226:  + @vitest/ui 4.0.17
227:  + convex 1.32.0
228:  + eslint 8.57.1
229:  + eslint-plugin-react 7.37.5
230:  + eslint-plugin-react-hooks 5.2.0
231:  + prettier 3.8.0
232:  + tsx 4.21.0
233:  + typescript 5.9.3
234:  + vitest 4.0.17
235:  + wrangler 4.66.0
236:  Done in 9.4s
237:  ##[group]Run missing=0
238:  �[36;1mmissing=0�[0m
239:  �[36;1mfor name in E2E_BACKEND_URL TEST_ADMIN_SECRET; do�[0m
240:  �[36;1m  if [ -z "${!name}" ]; then�[0m
241:  �[36;1m    echo "::error::Missing required secret: $name"�[0m
242:  �[36;1m    missing=1�[0m
...

268:  E2E_TEST_PASSWORD: 
269:  E2E_SUMMARY_PATH: artifacts/e2e-summary.json
270:  E2E_RELIABILITY_REPORT_PATH: artifacts/e2e-reliability-report.json
271:  E2E_RELIABILITY_BUDGET_PATH: security/e2e-reliability-budget.json
272:  E2E_RELIABILITY_ALLOWLIST_PATH: security/e2e-reliability-allowlist.json
273:  TEST_RUN_ID: ci-23007355084-1
274:  PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin
275:  ##[endgroup]
276:  > opencom@0.1.0 web:test:e2e /home/runner/work/opencom/opencom
277:  > set -a; source packages/convex/.env.local; set +a; pnpm playwright test
278:  sh: 1: source: not found
279:  [WebServer] 
280:  [WebServer]  �[33m�[1m⚠�[22m�[39m The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/app/api-reference/config/eslint#migrating-existing-config
281:  Running 190 tests using 4 workers
282:  ✘    1 [chromium] › apps/web/e2e/carousels.spec.ts:87:7 › Web Admin - Carousel Management › runs deterministic activate/pause/duplicate/delete lifecycle (1ms)
283:  -    5 [chromium] › apps/web/e2e/carousels.spec.ts:137:7 › Web Admin - Carousel Management › surfaces editor validation errors for CTA URLs and deep links
284:  ✘    2 [chromium] › apps/web/e2e/ai-agent.spec.ts:123:7 › Inbox AI deterministic workflow › shows AI-handled review metadata and deep-links to the source message (1ms)
285:  -    6 [chromium] › apps/web/e2e/ai-agent.spec.ts:170:7 › Inbox AI deterministic workflow › filters handoff conversations and shows handoff reason consistency
286:  ✘    3 [chromium] › apps/web/e2e/ai-agent-settings.spec.ts:59:7 › Web Admin - AI Agent Settings › should display AI Agent section on settings page (2ms)
287:  ✘    4 [chromium] › apps/web/e2e/audit-logs.spec.ts:107:7 › Web Admin - Audit Logs › should navigate to audit logs page (2ms)
288:  -    7 [chromium] › apps/web/e2e/audit-logs.spec.ts:114:7 › Web Admin - Audit Logs › should display seeded audit entries and show detail metadata
289:  -    8 [chromium] › apps/web/e2e/audit-logs.spec.ts:143:7 › Web Admin - Audit Logs › should filter audit logs by resource type
290:  -    9 [chromium] › apps/web/e2e/audit-logs.spec.ts:176:7 › Web Admin - Audit Logs › should trigger filtered export
291:  ✘   11 [chromium] › apps/web/e2e/carousels.spec.ts:87:7 › Web Admin - Carousel Management › runs deterministic activate/pause/duplicate/delete lifecycle (retry #1) (1ms)
292:  -   14 [chromium] › apps/web/e2e/carousels.spec.ts:137:7 › Web Admin - Carousel Management › surfaces editor validation errors for CTA URLs and deep links (retry #1)
293:  ✘   10 [chromium] › apps/web/e2e/audit-logs.spec.ts:107:7 › Web Admin - Audit Logs › should navigate to audit logs page (retry #1) (2ms)
294:  -   15 [chromium] › apps/web/e2e/audit-logs.spec.ts:114:7 › Web Admin - Audit Logs › should display seeded audit entries and show detail metadata (retry #1)
295:  -   16 [chromium] › apps/web/e2e/audit-logs.spec.ts:143:7 › Web Admin - Audit Logs › should filter audit logs by resource type (retry #1)
296:  -   17 [chromium] › apps/web/e2e/audit-logs.spec.ts:176:7 › Web Admin - Audit Logs › should trigger filtered export (retry #1)
297:  ✘   12 [chromium] › apps/web/e2e/ai-agent.spec.ts:123:7 › Inbox AI deterministic workflow › shows AI-handled review metadata and deep-links to the source message (retry #1) (1ms)
298:  -   18 [chromium] › apps/web/e2e/ai-agent.spec.ts:170:7 › Inbox AI deterministic workflow › filters handoff conversations and shows handoff reason consistency (retry #1)
299:  ✘   13 [chromium] › apps/web/e2e/ai-agent-settings.spec.ts:59:7 › Web Admin - AI Agent Settings › should display AI Agent section on settings page (retry #1) (5ms)
300:  ✘   19 [chromium] › apps/web/e2e/ai-agent.spec.ts:123:7 › Inbox AI deterministic workflow › shows AI-handled review metadata and deep-links to the source message (retry #2) (1ms)
301:  -   22 [chromium] › apps/web/e2e/ai-agent.spec.ts:170:7 › Inbox AI deterministic workflow › filters handoff conversations and shows handoff reason consistency (retry #2)
302:  ✘   20 [chromium] › apps/web/e2e/audit-logs.spec.ts:107:7 › Web Admin - Audit Logs › should navigate to audit logs page (retry #2) (2ms)
303:  -   23 [chromium] › apps/web/e2e/audit-logs.spec.ts:114:7 › Web Admin - Audit Logs › should display seeded audit entries and show detail metadata (retry #2)
304:  -   24 [chromium] › apps/web/e2e/audit-logs.spec.ts:143:7 › Web Admin - Audit Logs › should filter audit logs by resource type (retry #2)
305:  -   25 [chromium] › apps/web/e2e/audit-logs.spec.ts:176:7 › Web Admin - Audit Logs › should trigger filtered export (retry #2)
306:  ✘   21 [chromium] › apps/web/e2e/carousels.spec.ts:87:7 › Web Admin - Carousel Management › runs deterministic activate/pause/duplicate/delete lifecycle (retry #2) (1ms)
307:  -   27 [chromium] › apps/web/e2e/carousels.spec.ts:137:7 › Web Admin - Carousel Management › surfaces editor validation errors for CTA URLs and deep links (retry #2)
308:  ✘   26 [chromium] › apps/web/e2e/ai-agent-settings.spec.ts:59:7 › Web Admin - AI Agent Settings › should display AI Agent section on settings page (retry #2) (2ms)
...

841:  ✘  560 [chromium] › apps/web/e2e/outbound.spec.ts:617:7 › Series Builder › should gate activation with readiness blockers until valid (1ms)
842:  ✘  561 [chromium] › apps/web/e2e/outbound.spec.ts:617:7 › Series Builder › should gate activation with readiness blockers until valid (retry #1) (1ms)
843:  ✘  562 [chromium] › apps/web/e2e/outbound.spec.ts:617:7 › Series Builder › should gate activation with readiness blockers until valid (retry #2) (1ms)
844:  ✘  563 [chromium] › apps/web/e2e/outbound.spec.ts:635:7 › Series Builder › should support connection authoring with explicit default branch labels (1ms)
845:  ✘  564 [chromium] › apps/web/e2e/outbound.spec.ts:635:7 › Series Builder › should support connection authoring with explicit default branch labels (retry #1) (1ms)
846:  ✘  565 [chromium] › apps/web/e2e/outbound.spec.ts:635:7 › Series Builder › should support connection authoring with explicit default branch labels (retry #2) (1ms)
847:  ✘  566 [chromium] › apps/web/e2e/outbound.spec.ts:678:7 › Series Builder › should validate global rule editors before save (1ms)
848:  ✘  567 [chromium] › apps/web/e2e/outbound.spec.ts:678:7 › Series Builder › should validate global rule editors before save (retry #1) (1ms)
849:  ✘  568 [chromium] › apps/web/e2e/outbound.spec.ts:678:7 › Series Builder › should validate global rule editors before save (retry #2) (1ms)
850:  🧹 Cleaning up E2E test environment...
851:  ℹ️ Shared test state not found; continuing cleanup via test email domain match
852:  🗑️ Deleting test data...
853:  ℹ️ No test data to clean up
854:  🎉 Teardown complete!
855:  1) [chromium] › apps/web/e2e/ai-agent-settings.spec.ts:59:7 › Web Admin - AI Agent Settings › should display AI Agent section on settings page 
856:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
857:  ╔═════════════════════════════════════════════════════════════════════════╗
858:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
859:  ║ Please run the following command to download new browsers:              ║
860:  ║                                                                         ║
861:  ║     pnpm exec playwright install                                        ║
862:  ║                                                                         ║
863:  ║ <3 Playwright Team                                                      ║
864:  ╚═════════════════════════════════════════════════════════════════════════╝
865:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
866:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
867:  ╔═════════════════════════════════════════════════════════════════════════╗
868:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
869:  ║ Please run the following command to download new browsers:              ║
870:  ║                                                                         ║
871:  ║     pnpm exec playwright install                                        ║
872:  ║                                                                         ║
873:  ║ <3 Playwright Team                                                      ║
874:  ╚═════════════════════════════════════════════════════════════════════════╝
875:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
876:  test-results/ai-agent-settings-Web-Admi-a8388-nt-section-on-settings-page-chromium-retry1/trace.zip
877:  Usage:
878:  pnpm exec playwright show-trace test-results/ai-agent-settings-Web-Admi-a8388-nt-section-on-settings-page-chromium-retry1/trace.zip
879:  ────────────────────────────────────────────────────────────────────────────────────────────────
880:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
881:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
882:  ╔═════════════════════════════════════════════════════════════════════════╗
883:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
884:  ║ Please run the following command to download new browsers:              ║
885:  ║                                                                         ║
886:  ║     pnpm exec playwright install                                        ║
887:  ║                                                                         ║
888:  ║ <3 Playwright Team                                                      ║
889:  ╚═════════════════════════════════════════════════════════════════════════╝
890:  2) [chromium] › apps/web/e2e/ai-agent-settings.spec.ts:65:7 › Web Admin - AI Agent Settings › should toggle AI agent enable/disable 
891:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
892:  ╔═════════════════════════════════════════════════════════════════════════╗
893:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
894:  ║ Please run the following command to download new browsers:              ║
895:  ║                                                                         ║
896:  ║     pnpm exec playwright install                                        ║
897:  ║                                                                         ║
898:  ║ <3 Playwright Team                                                      ║
899:  ╚═════════════════════════════════════════════════════════════════════════╝
900:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
901:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
902:  ╔═════════════════════════════════════════════════════════════════════════╗
903:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
904:  ║ Please run the following command to download new browsers:              ║
905:  ║                                                                         ║
906:  ║     pnpm exec playwright install                                        ║
907:  ║                                                                         ║
908:  ║ <3 Playwright Team                                                      ║
909:  ╚═════════════════════════════════════════════════════════════════════════╝
910:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
911:  test-results/ai-agent-settings-Web-Admi-09aa3-gle-AI-agent-enable-disable-chromium-retry1/trace.zip
912:  Usage:
913:  pnpm exec playwright show-trace test-results/ai-agent-settings-Web-Admi-09aa3-gle-AI-agent-enable-disable-chromium-retry1/trace.zip
914:  ────────────────────────────────────────────────────────────────────────────────────────────────
915:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
916:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
917:  ╔═════════════════════════════════════════════════════════════════════════╗
918:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
919:  ║ Please run the following command to download new browsers:              ║
920:  ║                                                                         ║
921:  ║     pnpm exec playwright install                                        ║
922:  ║                                                                         ║
923:  ║ <3 Playwright Team                                                      ║
924:  ╚═════════════════════════════════════════════════════════════════════════╝
925:  3) [chromium] › apps/web/e2e/ai-agent-settings.spec.ts:81:7 › Web Admin - AI Agent Settings › should save AI agent settings 
926:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
927:  ╔═════════════════════════════════════════════════════════════════════════╗
928:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
929:  ║ Please run the following command to download new browsers:              ║
930:  ║                                                                         ║
931:  ║     pnpm exec playwright install                                        ║
932:  ║                                                                         ║
933:  ║ <3 Playwright Team                                                      ║
934:  ╚═════════════════════════════════════════════════════════════════════════╝
935:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
936:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
937:  ╔═════════════════════════════════════════════════════════════════════════╗
938:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
939:  ║ Please run the following command to download new browsers:              ║
940:  ║                                                                         ║
941:  ║     pnpm exec playwright install                                        ║
942:  ║                                                                         ║
943:  ║ <3 Playwright Team                                                      ║
944:  ╚═════════════════════════════════════════════════════════════════════════╝
945:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
946:  test-results/ai-agent-settings-Web-Admi-92d18-ould-save-AI-agent-settings-chromium-retry1/trace.zip
947:  Usage:
948:  pnpm exec playwright show-trace test-results/ai-agent-settings-Web-Admi-92d18-ould-save-AI-agent-settings-chromium-retry1/trace.zip
949:  ────────────────────────────────────────────────────────────────────────────────────────────────
950:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
951:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
952:  ╔═════════════════════════════════════════════════════════════════════════╗
953:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
954:  ║ Please run the following command to download new browsers:              ║
955:  ║                                                                         ║
956:  ║     pnpm exec playwright install                                        ║
957:  ║                                                                         ║
958:  ║ <3 Playwright Team                                                      ║
959:  ╚═════════════════════════════════════════════════════════════════════════╝
960:  4) [chromium] › apps/web/e2e/ai-agent-settings.spec.ts:93:7 › Web Admin - AI Agent Settings › should display AI personality settings when enabled 
961:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
962:  ╔═════════════════════════════════════════════════════════════════════════╗
963:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
964:  ║ Please run the following command to download new browsers:              ║
965:  ║                                                                         ║
966:  ║     pnpm exec playwright install                                        ║
967:  ║                                                                         ║
968:  ║ <3 Playwright Team                                                      ║
969:  ╚═════════════════════════════════════════════════════════════════════════╝
970:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
971:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
972:  ╔═════════════════════════════════════════════════════════════════════════╗
973:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
974:  ║ Please run the following command to download new browsers:              ║
975:  ║                                                                         ║
976:  ║     pnpm exec playwright install                                        ║
977:  ║                                                                         ║
978:  ║ <3 Playwright Team                                                      ║
979:  ╚═════════════════════════════════════════════════════════════════════════╝
980:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
981:  test-results/ai-agent-settings-Web-Admi-544e7-ality-settings-when-enabled-chromium-retry1/trace.zip
982:  Usage:
983:  pnpm exec playwright show-trace test-results/ai-agent-settings-Web-Admi-544e7-ality-settings-when-enabled-chromium-retry1/trace.zip
984:  ────────────────────────────────────────────────────────────────────────────────────────────────
985:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
986:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
987:  ╔═════════════════════════════════════════════════════════════════════════╗
988:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
989:  ║ Please run the following command to download new browsers:              ║
990:  ║                                                                         ║
991:  ║     pnpm exec playwright install                                        ║
992:  ║                                                                         ║
993:  ║ <3 Playwright Team                                                      ║
994:  ╚═════════════════════════════════════════════════════════════════════════╝
995:  5) [chromium] › apps/web/e2e/ai-agent.spec.ts:123:7 › Inbox AI deterministic workflow › shows AI-handled review metadata and deep-links to the source message 
996:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
997:  ╔═════════════════════════════════════════════════════════════════════════╗
998:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
999:  ║ Please run the following command to download new browsers:              ║
1000:  ║                                                                         ║
1001:  ║     pnpm exec playwright install                                        ║
1002:  ║                                                                         ║
1003:  ║ <3 Playwright Team                                                      ║
1004:  ╚═════════════════════════════════════════════════════════════════════════╝
1005:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1006:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1007:  ╔═════════════════════════════════════════════════════════════════════════╗
1008:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1009:  ║ Please run the following command to download new browsers:              ║
1010:  ║                                                                         ║
1011:  ║     pnpm exec playwright install                                        ║
1012:  ║                                                                         ║
1013:  ║ <3 Playwright Team                                                      ║
1014:  ╚═════════════════════════════════════════════════════════════════════════╝
1015:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1016:  test-results/ai-agent-Inbox-AI-determin-3c303-links-to-the-source-message-chromium-retry1/trace.zip
1017:  Usage:
1018:  pnpm exec playwright show-trace test-results/ai-agent-Inbox-AI-determin-3c303-links-to-the-source-message-chromium-retry1/trace.zip
1019:  ────────────────────────────────────────────────────────────────────────────────────────────────
1020:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1021:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1022:  ╔═════════════════════════════════════════════════════════════════════════╗
1023:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1024:  ║ Please run the following command to download new browsers:              ║
1025:  ║                                                                         ║
1026:  ║     pnpm exec playwright install                                        ║
1027:  ║                                                                         ║
1028:  ║ <3 Playwright Team                                                      ║
1029:  ╚═════════════════════════════════════════════════════════════════════════╝
1030:  6) [chromium] › apps/web/e2e/audit-logs.spec.ts:107:7 › Web Admin - Audit Logs › should navigate to audit logs page 
1031:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1032:  ╔═════════════════════════════════════════════════════════════════════════╗
1033:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1034:  ║ Please run the following command to download new browsers:              ║
1035:  ║                                                                         ║
1036:  ║     pnpm exec playwright install                                        ║
1037:  ║                                                                         ║
1038:  ║ <3 Playwright Team                                                      ║
1039:  ╚═════════════════════════════════════════════════════════════════════════╝
1040:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1041:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1042:  ╔═════════════════════════════════════════════════════════════════════════╗
1043:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1044:  ║ Please run the following command to download new browsers:              ║
1045:  ║                                                                         ║
1046:  ║     pnpm exec playwright install                                        ║
1047:  ║                                                                         ║
1048:  ║ <3 Playwright Team                                                      ║
1049:  ╚═════════════════════════════════════════════════════════════════════════╝
1050:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1051:  test-results/audit-logs-Web-Admin---Aud-bd280-navigate-to-audit-logs-page-chromium-retry1/trace.zip
1052:  Usage:
1053:  pnpm exec playwright show-trace test-results/audit-logs-Web-Admin---Aud-bd280-navigate-to-audit-logs-page-chromium-retry1/trace.zip
1054:  ────────────────────────────────────────────────────────────────────────────────────────────────
1055:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1056:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1057:  ╔═════════════════════════════════════════════════════════════════════════╗
1058:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1059:  ║ Please run the following command to download new browsers:              ║
1060:  ║                                                                         ║
1061:  ║     pnpm exec playwright install                                        ║
1062:  ║                                                                         ║
1063:  ║ <3 Playwright Team                                                      ║
1064:  ╚═════════════════════════════════════════════════════════════════════════╝
1065:  7) [chromium] › apps/web/e2e/carousels.spec.ts:87:7 › Web Admin - Carousel Management › runs deterministic activate/pause/duplicate/delete lifecycle 
1066:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1067:  ╔═════════════════════════════════════════════════════════════════════════╗
1068:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1069:  ║ Please run the following command to download new browsers:              ║
1070:  ║                                                                         ║
1071:  ║     pnpm exec playwright install                                        ║
1072:  ║                                                                         ║
1073:  ║ <3 Playwright Team                                                      ║
1074:  ╚═════════════════════════════════════════════════════════════════════════╝
1075:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1076:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1077:  ╔═════════════════════════════════════════════════════════════════════════╗
1078:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1079:  ║ Please run the following command to download new browsers:              ║
1080:  ║                                                                         ║
1081:  ║     pnpm exec playwright install                                        ║
1082:  ║                                                                         ║
1083:  ║ <3 Playwright Team                                                      ║
1084:  ╚═════════════════════════════════════════════════════════════════════════╝
1085:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1086:  test-results/carousels-Web-Admin---Caro-e46cf--duplicate-delete-lifecycle-chromium-retry1/trace.zip
1087:  Usage:
1088:  pnpm exec playwright show-trace test-results/carousels-Web-Admin---Caro-e46cf--duplicate-delete-lifecycle-chromium-retry1/trace.zip
1089:  ────────────────────────────────────────────────────────────────────────────────────────────────
1090:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1091:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1092:  ╔═════════════════════════════════════════════════════════════════════════╗
1093:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1094:  ║ Please run the following command to download new browsers:              ║
1095:  ║                                                                         ║
1096:  ║     pnpm exec playwright install                                        ║
1097:  ║                                                                         ║
1098:  ║ <3 Playwright Team                                                      ║
1099:  ╚═════════════════════════════════════════════════════════════════════════╝
1100:  8) [chromium] › apps/web/e2e/chat.spec.ts:153:9 › Inbox chat responsiveness › chat controls remain usable on desktop viewport 
1101:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1102:  ╔═════════════════════════════════════════════════════════════════════════╗
1103:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1104:  ║ Please run the following command to download new browsers:              ║
1105:  ║                                                                         ║
1106:  ║     pnpm exec playwright install                                        ║
1107:  ║                                                                         ║
1108:  ║ <3 Playwright Team                                                      ║
1109:  ╚═════════════════════════════════════════════════════════════════════════╝
1110:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1111:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1112:  ╔═════════════════════════════════════════════════════════════════════════╗
1113:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1114:  ║ Please run the following command to download new browsers:              ║
1115:  ║                                                                         ║
1116:  ║     pnpm exec playwright install                                        ║
1117:  ║                                                                         ║
1118:  ║ <3 Playwright Team                                                      ║
1119:  ╚═════════════════════════════════════════════════════════════════════════╝
1120:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1121:  test-results/chat-Inbox-chat-responsive-e3ec2--usable-on-desktop-viewport-chromium-retry1/trace.zip
1122:  Usage:
1123:  pnpm exec playwright show-trace test-results/chat-Inbox-chat-responsive-e3ec2--usable-on-desktop-viewport-chromium-retry1/trace.zip
1124:  ────────────────────────────────────────────────────────────────────────────────────────────────
1125:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1126:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1127:  ╔═════════════════════════════════════════════════════════════════════════╗
1128:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1129:  ║ Please run the following command to download new browsers:              ║
1130:  ║                                                                         ║
1131:  ║     pnpm exec playwright install                                        ║
1132:  ║                                                                         ║
1133:  ║ <3 Playwright Team                                                      ║
1134:  ╚═════════════════════════════════════════════════════════════════════════╝
1135:  9) [chromium] › apps/web/e2e/csat.spec.ts:137:9 › CSAT deterministic lifecycle › shows CSAT prompt interaction on desktop viewport 
1136:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1137:  ╔═════════════════════════════════════════════════════════════════════════╗
1138:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1139:  ║ Please run the following command to download new browsers:              ║
1140:  ║                                                                         ║
1141:  ║     pnpm exec playwright install                                        ║
1142:  ║                                                                         ║
1143:  ║ <3 Playwright Team                                                      ║
1144:  ╚═════════════════════════════════════════════════════════════════════════╝
1145:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1146:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1147:  ╔═════════════════════════════════════════════════════════════════════════╗
1148:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1149:  ║ Please run the following command to download new browsers:              ║
1150:  ║                                                                         ║
1151:  ║     pnpm exec playwright install                                        ║
1152:  ║                                                                         ║
1153:  ║ <3 Playwright Team                                                      ║
1154:  ╚═════════════════════════════════════════════════════════════════════════╝
1155:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1156:  test-results/csat-CSAT-deterministic-li-f6c78-raction-on-desktop-viewport-chromium-retry1/trace.zip
1157:  Usage:
1158:  pnpm exec playwright show-trace test-results/csat-CSAT-deterministic-li-f6c78-raction-on-desktop-viewport-chromium-retry1/trace.zip
1159:  ────────────────────────────────────────────────────────────────────────────────────────────────
1160:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1161:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1162:  ╔═════════════════════════════════════════════════════════════════════════╗
1163:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1164:  ║ Please run the following command to download new browsers:              ║
1165:  ║                                                                         ║
1166:  ║     pnpm exec playwright install                                        ║
1167:  ║                                                                         ║
1168:  ║ <3 Playwright Team                                                      ║
1169:  ╚═════════════════════════════════════════════════════════════════════════╝
1170:  10) [chromium] › apps/web/e2e/help-center-import.spec.ts:22:7 › Help Center Markdown Import › imports docs folder and shows articles in help center 
1171:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1172:  ╔═════════════════════════════════════════════════════════════════════════╗
1173:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1174:  ║ Please run the following command to download new browsers:              ║
1175:  ║                                                                         ║
1176:  ║     pnpm exec playwright install                                        ║
1177:  ║                                                                         ║
1178:  ║ <3 Playwright Team                                                      ║
1179:  ╚═════════════════════════════════════════════════════════════════════════╝
1180:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1181:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1182:  ╔═════════════════════════════════════════════════════════════════════════╗
1183:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1184:  ║ Please run the following command to download new browsers:              ║
1185:  ║                                                                         ║
1186:  ║     pnpm exec playwright install                                        ║
1187:  ║                                                                         ║
1188:  ║ <3 Playwright Team                                                      ║
1189:  ╚═════════════════════════════════════════════════════════════════════════╝
1190:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1191:  test-results/help-center-import-Help-Ce-36ada-ows-articles-in-help-center-chromium-retry1/trace.zip
1192:  Usage:
1193:  pnpm exec playwright show-trace test-results/help-center-import-Help-Ce-36ada-ows-articles-in-help-center-chromium-retry1/trace.zip
1194:  ────────────────────────────────────────────────────────────────────────────────────────────────
1195:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1196:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1197:  ╔═════════════════════════════════════════════════════════════════════════╗
1198:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1199:  ║ Please run the following command to download new browsers:              ║
1200:  ║                                                                         ║
1201:  ║     pnpm exec playwright install                                        ║
1202:  ║                                                                         ║
1203:  ║ <3 Playwright Team                                                      ║
1204:  ╚═════════════════════════════════════════════════════════════════════════╝
1205:  11) [chromium] › apps/web/e2e/home-settings.spec.ts:74:7 › Web Admin - Home Settings › should load home settings section on settings page 
1206:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1207:  ╔═════════════════════════════════════════════════════════════════════════╗
1208:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1209:  ║ Please run the following command to download new browsers:              ║
1210:  ║                                                                         ║
1211:  ║     pnpm exec playwright install                                        ║
1212:  ║                                                                         ║
1213:  ║ <3 Playwright Team                                                      ║
1214:  ╚═════════════════════════════════════════════════════════════════════════╝
1215:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1216:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1217:  ╔═════════════════════════════════════════════════════════════════════════╗
1218:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1219:  ║ Please run the following command to download new browsers:              ║
1220:  ║                                                                         ║
1221:  ║     pnpm exec playwright install                                        ║
1222:  ║                                                                         ║
1223:  ║ <3 Playwright Team                                                      ║
1224:  ╚═════════════════════════════════════════════════════════════════════════╝
1225:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1226:  test-results/home-settings-Web-Admin----e53d4-gs-section-on-settings-page-chromium-retry1/trace.zip
1227:  Usage:
1228:  pnpm exec playwright show-trace test-results/home-settings-Web-Admin----e53d4-gs-section-on-settings-page-chromium-retry1/trace.zip
1229:  ────────────────────────────────────────────────────────────────────────────────────────────────
1230:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1231:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1232:  ╔═════════════════════════════════════════════════════════════════════════╗
1233:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1234:  ║ Please run the following command to download new browsers:              ║
1235:  ║                                                                         ║
1236:  ║     pnpm exec playwright install                                        ║
1237:  ║                                                                         ║
1238:  ║ <3 Playwright Team                                                      ║
1239:  ╚═════════════════════════════════════════════════════════════════════════╝
1240:  12) [chromium] › apps/web/e2e/home-settings.spec.ts:79:7 › Web Admin - Home Settings › should toggle home enabled/disabled 
1241:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1242:  ╔═════════════════════════════════════════════════════════════════════════╗
1243:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1244:  ║ Please run the following command to download new browsers:              ║
1245:  ║                                                                         ║
1246:  ║     pnpm exec playwright install                                        ║
1247:  ║                                                                         ║
1248:  ║ <3 Playwright Team                                                      ║
1249:  ╚═════════════════════════════════════════════════════════════════════════╝
1250:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1251:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1252:  ╔═════════════════════════════════════════════════════════════════════════╗
1253:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1254:  ║ Please run the following command to download new browsers:              ║
1255:  ║                                                                         ║
1256:  ║     pnpm exec playwright install                                        ║
1257:  ║                                                                         ║
1258:  ║ <3 Playwright Team                                                      ║
1259:  ╚═════════════════════════════════════════════════════════════════════════╝
1260:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1261:  test-results/home-settings-Web-Admin----2a5d3-oggle-home-enabled-disabled-chromium-retry1/trace.zip
1262:  Usage:
1263:  pnpm exec playwright show-trace test-results/home-settings-Web-Admin----2a5d3-oggle-home-enabled-disabled-chromium-retry1/trace.zip
1264:  ────────────────────────────────────────────────────────────────────────────────────────────────
1265:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1266:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1267:  ╔═════════════════════════════════════════════════════════════════════════╗
1268:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1269:  ║ Please run the following command to download new browsers:              ║
1270:  ║                                                                         ║
1271:  ║     pnpm exec playwright install                                        ║
1272:  ║                                                                         ║
1273:  ║ <3 Playwright Team                                                      ║
1274:  ╚═════════════════════════════════════════════════════════════════════════╝
1275:  13) [chromium] › apps/web/e2e/home-settings.spec.ts:86:7 › Web Admin - Home Settings › should add a card to home configuration 
1276:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1277:  ╔═════════════════════════════════════════════════════════════════════════╗
1278:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1279:  ║ Please run the following command to download new browsers:              ║
1280:  ║                                                                         ║
1281:  ║     pnpm exec playwright install                                        ║
1282:  ║                                                                         ║
1283:  ║ <3 Playwright Team                                                      ║
1284:  ╚═════════════════════════════════════════════════════════════════════════╝
1285:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1286:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1287:  ╔═════════════════════════════════════════════════════════════════════════╗
1288:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1289:  ║ Please run the following command to download new browsers:              ║
1290:  ║                                                                         ║
1291:  ║     pnpm exec playwright install                                        ║
1292:  ║                                                                         ║
1293:  ║ <3 Playwright Team                                                      ║
1294:  ╚═════════════════════════════════════════════════════════════════════════╝
1295:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1296:  test-results/home-settings-Web-Admin----50ca2--card-to-home-configuration-chromium-retry1/trace.zip
1297:  Usage:
1298:  pnpm exec playwright show-trace test-results/home-settings-Web-Admin----50ca2--card-to-home-configuration-chromium-retry1/trace.zip
1299:  ────────────────────────────────────────────────────────────────────────────────────────────────
1300:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1301:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1302:  ╔═════════════════════════════════════════════════════════════════════════╗
1303:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1304:  ║ Please run the following command to download new browsers:              ║
1305:  ║                                                                         ║
1306:  ║     pnpm exec playwright install                                        ║
1307:  ║                                                                         ║
1308:  ║ <3 Playwright Team                                                      ║
1309:  ╚═════════════════════════════════════════════════════════════════════════╝
1310:  14) [chromium] › apps/web/e2e/home-settings.spec.ts:106:7 › Web Admin - Home Settings › should change card visibility setting 
1311:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1312:  ╔═════════════════════════════════════════════════════════════════════════╗
1313:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1314:  ║ Please run the following command to download new browsers:              ║
1315:  ║                                                                         ║
1316:  ║     pnpm exec playwright install                                        ║
1317:  ║                                                                         ║
1318:  ║ <3 Playwright Team                                                      ║
1319:  ╚═════════════════════════════════════════════════════════════════════════╝
1320:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1321:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1322:  ╔═════════════════════════════════════════════════════════════════════════╗
1323:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1324:  ║ Please run the following command to download new browsers:              ║
1325:  ║                                                                         ║
1326:  ║     pnpm exec playwright install                                        ║
1327:  ║                                                                         ║
1328:  ║ <3 Playwright Team                                                      ║
1329:  ╚═════════════════════════════════════════════════════════════════════════╝
1330:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1331:  test-results/home-settings-Web-Admin----43b05-nge-card-visibility-setting-chromium-retry1/trace.zip
1332:  Usage:
1333:  pnpm exec playwright show-trace test-results/home-settings-Web-Admin----43b05-nge-card-visibility-setting-chromium-retry1/trace.zip
1334:  ────────────────────────────────────────────────────────────────────────────────────────────────
1335:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1336:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1337:  ╔═════════════════════════════════════════════════════════════════════════╗
1338:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1339:  ║ Please run the following command to download new browsers:              ║
1340:  ║                                                                         ║
1341:  ║     pnpm exec playwright install                                        ║
1342:  ║                                                                         ║
1343:  ║ <3 Playwright Team                                                      ║
1344:  ╚═════════════════════════════════════════════════════════════════════════╝
1345:  15) [chromium] › apps/web/e2e/home-settings.spec.ts:123:7 › Web Admin - Home Settings › should save home settings 
1346:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1347:  ╔═════════════════════════════════════════════════════════════════════════╗
1348:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1349:  ║ Please run the following command to download new browsers:              ║
1350:  ║                                                                         ║
1351:  ║     pnpm exec playwright install                                        ║
1352:  ║                                                                         ║
1353:  ║ <3 Playwright Team                                                      ║
1354:  ╚═════════════════════════════════════════════════════════════════════════╝
1355:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1356:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1357:  ╔═════════════════════════════════════════════════════════════════════════╗
1358:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1359:  ║ Please run the following command to download new browsers:              ║
1360:  ║                                                                         ║
1361:  ║     pnpm exec playwright install                                        ║
1362:  ║                                                                         ║
1363:  ║ <3 Playwright Team                                                      ║
1364:  ╚═════════════════════════════════════════════════════════════════════════╝
1365:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1366:  test-results/home-settings-Web-Admin----ee8d0-s-should-save-home-settings-chromium-retry1/trace.zip
1367:  Usage:
1368:  pnpm exec playwright show-trace test-results/home-settings-Web-Admin----ee8d0-s-should-save-home-settings-chromium-retry1/trace.zip
1369:  ────────────────────────────────────────────────────────────────────────────────────────────────
1370:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1371:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1372:  ╔═════════════════════════════════════════════════════════════════════════╗
1373:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1374:  ║ Please run the following command to download new browsers:              ║
1375:  ║                                                                         ║
1376:  ║     pnpm exec playwright install                                        ║
1377:  ║                                                                         ║
1378:  ║ <3 Playwright Team                                                      ║
1379:  ╚═════════════════════════════════════════════════════════════════════════╝
1380:  16) [chromium] › apps/web/e2e/home-settings.spec.ts:139:7 › Web Admin - Home Settings › should show home preview when cards are added 
1381:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1382:  ╔═════════════════════════════════════════════════════════════════════════╗
1383:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1384:  ║ Please run the following command to download new browsers:              ║
1385:  ║                                                                         ║
1386:  ║     pnpm exec playwright install                                        ║
1387:  ║                                                                         ║
1388:  ║ <3 Playwright Team                                                      ║
1389:  ╚═════════════════════════════════════════════════════════════════════════╝
1390:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1391:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1392:  ╔═════════════════════════════════════════════════════════════════════════╗
1393:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1394:  ║ Please run the following command to download new browsers:              ║
1395:  ║                                                                         ║
1396:  ║     pnpm exec playwright install                                        ║
1397:  ║                                                                         ║
1398:  ║ <3 Playwright Team                                                      ║
1399:  ╚═════════════════════════════════════════════════════════════════════════╝
1400:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1401:  test-results/home-settings-Web-Admin----e0e23-review-when-cards-are-added-chromium-retry1/trace.zip
1402:  Usage:
1403:  pnpm exec playwright show-trace test-results/home-settings-Web-Admin----e0e23-review-when-cards-are-added-chromium-retry1/trace.zip
1404:  ────────────────────────────────────────────────────────────────────────────────────────────────
1405:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1406:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1407:  ╔═════════════════════════════════════════════════════════════════════════╗
1408:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1409:  ║ Please run the following command to download new browsers:              ║
1410:  ║                                                                         ║
1411:  ║     pnpm exec playwright install                                        ║
1412:  ║                                                                         ║
1413:  ║ <3 Playwright Team                                                      ║
1414:  ╚═════════════════════════════════════════════════════════════════════════╝
1415:  17) [chromium] › apps/web/e2e/identity-verification.spec.ts:8:7 › Identity Verification Flow › should display security settings section 
1416:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1417:  ╔═════════════════════════════════════════════════════════════════════════╗
1418:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1419:  ║ Please run the following command to download new browsers:              ║
1420:  ║                                                                         ║
1421:  ║     pnpm exec playwright install                                        ║
1422:  ║                                                                         ║
1423:  ║ <3 Playwright Team                                                      ║
1424:  ╚═════════════════════════════════════════════════════════════════════════╝
1425:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
1426:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1427:  ╔═════════════════════════════════════════════════════════════════════════╗
1428:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1429:  ║ Please run the following command to download new browsers:              ║
1430:  ║                                                                         ║
1431:  ║     pnpm exec playwright install                                        ║
1432:  ║                                                                         ║
1433:  ║ <3 Playwright Team                                                      ║
1434:  ╚═════════════════════════════════════════════════════════════════════════╝
1435:  attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────
1436:  test-results/identity-verification-Iden-98297-y-security-settings-section-chromium-retry1/trace.zip
1437:  Usage:
1438:  pnpm exec playwright show-trace test-results/identity-verification-Iden-98297-y-security-settings-section-chromium-retry1/trace.zip
1439:  ────────────────────────────────────────────────────────────────────────────────────────────────
1440:  Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
1441:  Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium_headless_shell-1200/chrome-headless-shell-linux64/chrome-headless-shell
1442:  ╔═════════════════════════════════════════════════════════════════════════╗
1443:  ║ Looks like Playwright Test or Playwright was just installed or updated. ║
1444:  ║ Please run the following command t...

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end support-attachment uploads for inbox replies, widget chat, and ticket workflows by introducing a shared attachment domain, Convex backend support (schema + mutations), and UI rendering/compose-state management across web and widget clients.

Changes:

  • Introduces shared web-shared attachment helpers (validation, MIME inference, size formatting, upload flow).
  • Adds Convex supportAttachments module + schema updates and wires attachments into messages/tickets read + send/create/comment flows.
  • Updates Web Inbox, Web Tickets, and Widget chat/tickets UI to upload, preview, remove, and render attachments; adds/updates tests.

Reviewed changes

Copilot reviewed 40 out of 41 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/web-shared/src/supportAttachments.ts New shared attachment types + validation + upload helper.
packages/web-shared/src/index.ts Exports attachment helpers/types from web-shared.
packages/convex/tests/supportAttachments.test.ts Integration tests covering upload validation/auth/binding/cleanup.
packages/convex/tests/runtimeTypeHardeningGuard.test.ts Ensures new supportAttachments module follows typed-ref conventions.
packages/convex/convex/tickets.ts Binds staged attachments on ticket create/comment; resolves descriptors in queries.
packages/convex/convex/testing/helpers/supportAttachments.ts Test-only helpers for expiring/cleaning staged uploads and checking storage.
packages/convex/convex/testing/helpers/cleanup.ts Extends test workspace cleanup to delete support attachments + stored files.
packages/convex/convex/testing/helpers.ts Exposes new support-attachment test helpers in the testing API.
packages/convex/convex/testData/cleanup.ts Ensures e2e test-data cleanup also deletes attachments referenced by tickets/messages.
packages/convex/convex/supportAttachments.ts New backend module for upload URL generation, finalize, binding, descriptor resolution, cleanup.
packages/convex/convex/supportAttachmentTypes.ts Shared validators/constants for attachment limits and allowed MIME types.
packages/convex/convex/schema/supportAttachmentTables.ts Adds supportAttachments table and indexes.
packages/convex/convex/schema/outboundSupportTables.ts Adds attachmentIds fields to tickets and ticketComments schema.
packages/convex/convex/schema/inboxConversationTables.ts Adds attachmentIds to inbox message schema.
packages/convex/convex/schema.ts Registers the new support-attachment tables in the Convex schema.
packages/convex/convex/messages.ts Adds attachmentIds to send/list; binds staged uploads; resolves attachment descriptors in list results.
packages/convex/convex/_generated/api.d.ts Updates generated API typings to include support-attachment modules/schema.
openspec/changes/add-chat-and-ticket-file-uploads/tasks.md Marks related openspec tasks as completed.
apps/widget/src/styles.css Adds composer/attachment UI styles for widget chat and tickets.
apps/widget/src/icons.tsx Adds Paperclip icon.
apps/widget/src/hooks/useWidgetTicketFlow.ts Adds attachment state + upload/remove flow for ticket create/comment in widget.
apps/widget/src/hooks/convex/useConversationViewConvex.ts Exposes supportAttachment upload/finalize refs in widget conversation hook.
apps/widget/src/components/conversationView/types.ts Adds attachments to message type.
apps/widget/src/components/conversationView/MessageList.tsx Renders message attachments in widget conversation thread.
apps/widget/src/components/conversationView/Footer.tsx Adds attachment input, queued previews, disable states for widget chat composer.
apps/widget/src/components/conversationView/Footer.test.tsx Tests widget composer layout with pending attachments.
apps/widget/src/components/TicketDetail.tsx Renders ticket/comment attachments + queued reply attachments in widget ticket detail.
apps/widget/src/components/TicketDetail.test.tsx Tests rendering and removal of queued ticket-reply attachments.
apps/widget/src/components/TicketCreate.tsx Adds attachment upload + queued previews to widget ticket creation.
apps/widget/src/components/ConversationView.tsx Adds attachment upload + pending state + error handling to widget chat flow.
apps/widget/src/Widget.tsx Wires ticket attachment state/actions from hooks into ticket components.
apps/web/src/app/tickets/page.tsx Adds attachment upload + preview + error handling to web ticket create modal.
apps/web/src/app/tickets/hooks/useTicketsConvex.ts Adds upload/finalize refs and attachment descriptors to ticket query/mutation types.
apps/web/src/app/tickets/[id]/page.tsx Renders ticket/comment attachments; adds comment composer attachment upload/preview.
apps/web/src/app/inbox/page.tsx Adds pending attachments + upload/remove flow in inbox reply UI state.
apps/web/src/app/inbox/inboxRenderTypes.ts Adds support attachment descriptors to inbox message render type.
apps/web/src/app/inbox/hooks/useInboxMessageActions.ts Includes attachmentIds in send + improves optimistic preview for attachment-only replies.
apps/web/src/app/inbox/hooks/useInboxConvex.ts Exposes attachment upload/finalize mutations for inbox UI.
apps/web/src/app/inbox/InboxThreadPane.tsx Adds attachment upload button, pending previews/removal, and attachment rendering in thread.
apps/web/src/app/inbox/InboxThreadPane.test.tsx Updates snippet-control expectations + adds attachment rendering/interaction test.
apps/web/e2e/widget.spec.ts Skips an email-capture dismiss test due to UI change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 49 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Partially completed - the upload spoofing hardening is improved, but not all the way to magic-byte sniffing. We now require an allowlisted extension from the normalized filename on both the client and backend in supportAttachments.ts (line 70) and supportAttachments.ts (line 57), and added regression coverage in supportAttachments.test.ts (line 151) and supportAttachments.test.ts (line 8). I did not add signature checks, because the current finalizeUpload boundary is a Convex mutation and ctx.storage.get() is only available in actions. Doing true magic-byte validation would need a larger refactor of that finalize flow.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 52 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +109 to +112
return {
message: `"${file.name}" is larger than 10 MB.`,
nextAction: "Choose a smaller file and try again.",
};
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This oversize error message also interpolates file.name (which may be path-like). Consider using the normalized base file name here as well for consistency with the allowlist validation and to avoid displaying path fragments.

Copilot uses AI. Check for mistakes.
Comment on lines +70 to +72
export function getSupportAttachmentMimeType(file: Pick<File, "name" | "type">): string | null {
return inferMimeTypeFromFileName(normalizeSupportAttachmentFileName(file.name));
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getSupportAttachmentMimeType accepts Pick<File, "name" | "type"> but doesn't use type at all (it infers solely from the normalized file name). Consider removing type from the accepted shape or renaming the function to make the extension-based behavior explicit, to avoid callers assuming the browser-provided MIME type is being validated.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
return {
message: `Unsupported file type for "${file.name}".`,
nextAction: "Use PNG, JPG, GIF, WEBP, PDF, TXT, CSV, JSON, or ZIP files.",
};
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message uses file.name directly, which can include path-like prefixes (e.g. C:\\fakepath\\...). Since you already normalize file names for validation, consider using the normalized base name in user-facing messages to avoid leaking/printing path-like strings and to keep messaging consistent.

Copilot uses AI. Check for mistakes.
@djanogly djanogly merged commit 0929ee8 into dev Mar 12, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants