2f5178eb4f
142 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
112385fd9e |
Merge branch 'main' into chore/merge-4.13.0
Resolves 26 conflicts via manual review. Key decisions: - signature: kept fork's send-time architecture (PR #79), discarded upstream's editor-manipulation functions - WhatsApp incoming: combined fork's two-layer locking (source_id + contact phone) with upstream's blocked-contact drop. Fixed pre-existing regression where echoes were silently dropped - InstallationConfig: upstream's simplified coder (validated against legacy YAML-in-jsonb data) - schema.rb: regenerated, stripped kanban tables from other branches, restored f_unaccent SQL function |
||
|
|
5264de24b0
|
feat: migrations for document auto-sync [AI-141] (#14041)
# Pull Request Template ## Description Add migrations for document auto-sync Fixes # (issue) ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? locally ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules |
||
|
|
f422c83c26
|
feat: Add unified Call model for voice calling (#14026)
Adds a Call model to track voice call state across providers (Twilio, WhatsApp). This replaces storing call data in conversation.additional_attributes and provides a foundation for call analytics multi-call-per-conversation support, and future voice providers. --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> |
||
|
|
0592cccca9
|
fix: prevent lost custom_attributes updates from concurrent jsonb writes (#14040)
## Linear ticket https://linear.app/chatwoot/issue/CW-6834/billing-upgrade-didnt-work ## Description A `customer.subscription.updated` Stripe webhook for account 76162 returned 200 OK but did not persist the new `subscribed_quantity`. Root cause: a race condition between the webhook handler and `increment_response_usage` (Captain usage counter), both doing read-modify-write on the `custom_attributes` JSONB column. The webhook wrote `quantity: 6`, then a concurrent `save` from `increment_response_usage` overwrote the entire hash with stale data — restoring `quantity: 5`. Fix: use atomic `jsonb_set` so usage counter updates only touch the single key they care about, instead of rewriting the whole `custom_attributes` hash. `increment_custom_attribute` also performs the increment in SQL, making concurrent increments correct as well. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? - New regression spec in `handle_stripe_event_service_spec.rb` that simulates concurrent webhook + `increment_response_usage` and asserts both `subscribed_quantity` and `captain_responses_usage` survive - Existing account, billing, captain, and topup specs all pass locally ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
95463230cb
|
feat: sign webhooks for API channel and agentbots (#13892)
Account webhooks sign outgoing payloads with HMAC-SHA256, but agent bot and API inbox webhooks were delivered unsigned. This PR adds the same signing to both. Each model gets a dedicated `secret` column rather than reusing the agent bot's `access_token` (for API auth back into Chatwoot) or the API inbox's `hmac_token` (for inbound contact identity verification). These serve different trust boundaries and shouldn't be coupled — rotating a signing secret shouldn't invalidate API access or contact verification. The existing `Webhooks::Trigger` already signs when a secret is present, so the backend change is just passing `secret:` through to the jobs. Shared token logic is extracted into a `WebhookSecretable` concern included by `Webhook`, `AgentBot`, and `Channel::Api`. The frontend reuses the existing `AccessToken` component for secret display. Secrets are admin-only and excluded from enterprise audit logs. ### How to test Point an agent bot or API inbox webhook URL at a request inspector. Send a message and verify `X-Chatwoot-Signature` and `X-Chatwoot-Timestamp` headers are present. Reset the secret from settings and confirm subsequent deliveries use the new value. --------- Co-authored-by: Sojan Jose <sojan@pepalo.com> |
||
|
|
5fd3d5e036
|
feat: allow zero conversation limit capacity policy (#13964)
## Description Two improvements to Agent Capacity Policy: **1. Support exclusion via zero conversation limit** Allow `conversation_limit` to be `0` on inbox capacity limits. Agents with a zero limit are excluded from auto-assignment for that inbox while remaining members for manual assignment. **2. Fix exclusion rules duration input** - Default changed from `10` to `null` so time-based exclusion isn't applied unless explicitly set. - Minimum lowered from 10 to 1 minute. - `DurationInput` updated to handle `null` values correctly. ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - Added model and capacity service specs for zero-limit exclusion behavior. - Tested manually via UI flows ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> |
||
|
|
8daf6cf6cb
|
feat: captain custom tools v1 (#13890)
# Pull Request Template ## Description Adds custom tool support to v1 ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. <img width="1816" height="958" alt="CleanShot 2026-03-24 at 11 37 33@2x" src="https://github.com/user-attachments/assets/2777a953-8b65-4a2d-88ec-39f395b3fb47" /> <img width="378" height="488" alt="CleanShot 2026-03-24 at 11 38 18@2x" src="https://github.com/user-attachments/assets/f6973c99-efd0-40e4-90fe-4472a2f63cea" /> <img width="1884" height="1452" alt="CleanShot 2026-03-24 at 11 38 32@2x" src="https://github.com/user-attachments/assets/9fba4fc4-0c33-46da-888a-52ec6bad6130" /> ## Checklist: - [x] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> |
||
|
|
9efd554693
|
fix: resolve V2 capacity bypass in team assignment (#13904)
## Description When Assignment V2 is enabled, the V2 capacity policies (AgentCapacityPolicy / InboxCapacityLimit) are not respected during team-based assignment paths. The system falls back to the legacy V1 max_assignment_limit, and since V1 is deprecated and typically unconfigured in V2 setups, agents receive unlimited assignments regardless of their V2 capacity. Root cause: Inbox class directly defined member_ids_with_assignment_capacity, which shadowed the Enterprise::InboxAgentAvailability module override in Ruby's method resolution order (MRO). This made the V2 capacity check unreachable (dead code) for any code path using member_ids_with_assignment_capacity. ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? ⏺ Before the fix 1. Enable assignment_v2 + advanced_assignment on account 2. Create AgentCapacityPolicy with InboxCapacityLimit = 1 for an inbox 3. Assign the policy to an agent (e.g., John) 4. Create 1 open conversation assigned to John (now at capacity) 5. Create a new unassigned conversation in the same inbox 6. Assign a team (containing John) to that conversation 7. Result: John gets assigned despite being at capacity ⏺ After the fix Same steps 1–6. 7. Result: John is NOT assigned — conversation stays unassigned (no agents with capacity available) ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
290dd3abf5
|
feat: allow captain to access contact attributes (#13850)
# Pull Request Template ## Description Captain v1 does not have access to contact attributes. Added a toggle to let user choose if they want contact information available to Captain. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. Specs and locally <img width="1924" height="740" alt="CleanShot 2026-03-19 at 18 48 19@2x" src="https://github.com/user-attachments/assets/353cfeaa-cd58-40eb-89e7-d660a1dc1185" /> ![Uploading CleanShot 2026-03-19 at 18.53.26@2x.png…]() ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules |
||
|
|
8fcef79847 | Merge branch 'chatwoot/develop' into chore/merge-upstream-4.12.0 | ||
|
|
d3ce2a4cf9 | Merge branch 'main' into chore/merge-upstream-4.12.0 | ||
|
|
a996b920e8
|
feat: group conversations (#228)
* feat: add group and conversation types to contacts and conversations, and implement conversation group membership model * chore: add factory and specs for conversation group member model * chore: add group type checks and associations for contacts and conversations * refactor: remove scopes from ConversationGroupMember model * refactor: remove scopes from ConversationGroupMember model specs * refactor: enhance conversation type migration with concurrent indexing * feat: add is_active index and scopes to ConversationGroupMember model * feat: implement GroupConversationHandler for managing group conversations * feat: add group_type attribute to contact creation * fix: update WHATSAPP_CHANNEL_REGEX to allow up to 20 digits to handle group jid * feat: handle group JID format in remote_jid method * chore: update group contact info when finding or creating group contact * chore: refactor and implement contact message handling and message creation logic for baileys single contact conversation * feat: implement group message handling and metadata fetching in WhatsApp service * chore: add spec for group type handling in contact creation for individual and group contacts * chore: add specs for test scopes in conversation group members * chore: update documentation for sender phone extraction in group conversation handler * chore: move GroupConversationHandler concern to correct dir * chore: implement specs for recipient_id handling to individual and group contacts * chore: add group message handling specs for incoming messages * chore: enhance group message handling to prevent race conditions * chore: add group_metadata method to with error handling * chore: add test for sending messages to group recipients in WhatsappBaileysService * chore: raise error for unsuccessful response in group_metadata method * chore: adds tests for group metadata retrieval and error handling * chore: refactor build_sender_contact_attributes to avoid double call methods * chore: update error handling for attachment download failure in message creation * chore: optimize update_contact_info method to use compact hash for updates * chore: simplify find_or_create_sender_contact method return values * chore: rename group and individual contact message handlers * chore: remove pointless comments from group contact message handler methods * chore: refine sender JID extraction logic to remove unnecessary checks * chore: remove phone number in spec for group contact attributes * chore: implement sync_group route * chore: implement get group_members route * fix: sync_group participants creation handling * chore: update contact avatar handling in group message processing * chore: move sync_group functionality for conversation model * feat: add sync_group action to ConversationsController and route * fix: set contact name to phone in group message processing * chore: refine group member retrieval logic in sync_group service and view * feat: implement group participants update handling * feat: implement group updates handling and localization for group activities * chore: add handling for group membership requests and icon changes * chore: add authorization for sync_group action in ContactsController * chore: add sync_group endpoint specs for contact management * chore: add authorization for sync_group action in ConversationsController * chore: add specs for sync_group endpoint in ConversationsController * chore: refactor index action in GroupMembersController for improved conversation filtering * chore: add request specs for group_members endpoint in ContactsController * chore: add specs for sync_group method in Conversation model * chore: add specs for sync_group method in Channel::Whatsapp model * chore: remove comments in find_or_create_group_conversation method * chore: add specs for Contacts::SyncGroupService to validate group contact behavior * chore: add specs for Whatsapp::BaileysHandlers::GroupsUpdate to validate group updates * chore: add specs for Whatsapp::BaileysHandlers::GroupParticipantsUpdate to handle group participant actions * chore: add fallback for identifier when contact has no phone_number in SendOnWhatsappService * chore: add specs for group membership request and icon change handling in MessagesUpsert * chore: add specs for sync_group method to handle group metadata and participant updates * chore: update sync_group method to retrieve group members and adjust JSON response * chore: update conversation query to filter by group type in GroupMembersController * chore: update conversation creation in group_members_controller_spec to specify conversation_type as group * chore: update find_or_create_group_conversation to include pending conversations * chore: refactor sync_group method and enhance specs for group conversation handling * feat: add GroupEventHelper module for managing group activities and contacts * chore: refactor group contact inbox and conversation creation methods in group handlers * chore: remove unnecessary check for blank participant contacts in sync_group_members method * feat: implement message receipt update handling for WhatsApp integration * chore: resolve rubocop rule for update_last_seen_at method * chore: update swagger with endpoints for syncing group information and listing group members * chore: integrate Contacts::SyncGroupService in group members controller, enhance error handling and update swagger * chore: include participant information in reaction and quoted message keys for send message in group conversations * chore: enhance whatsapp_baileys_service with participant handling for message keys * feat: add skill for writing RSpec tests in the project * fix: update recipient_id logic to directly use contact identifier for group contacts * chore: implement group stub message handling for membership requests and icon changes * fix: update whatsapp inbox source_id validation regex spec * chore: fix spec for contact syncing group * chore: remove readTimestamp handling and related tests for message read updates in group * Cayo oliveira/cu 86af01932/4 backend gerenciamento dos grupos (#221) * feat: add is_active index and scopes to ConversationGroupMember model * feat: implement GroupConversationHandler for managing group conversations * feat: add group_type attribute to contact creation * fix: update WHATSAPP_CHANNEL_REGEX to allow up to 20 digits to handle group jid * feat: handle group JID format in remote_jid method * chore: update group contact info when finding or creating group contact * chore: refactor and implement contact message handling and message creation logic for baileys single contact conversation * feat: implement group message handling and metadata fetching in WhatsApp service * chore: add spec for group type handling in contact creation for individual and group contacts * chore: add specs for test scopes in conversation group members * chore: update documentation for sender phone extraction in group conversation handler * chore: move GroupConversationHandler concern to correct dir * chore: implement specs for recipient_id handling to individual and group contacts * chore: add group message handling specs for incoming messages * chore: enhance group message handling to prevent race conditions * chore: add group_metadata method to with error handling * chore: add test for sending messages to group recipients in WhatsappBaileysService * chore: raise error for unsuccessful response in group_metadata method * chore: adds tests for group metadata retrieval and error handling * chore: refactor build_sender_contact_attributes to avoid double call methods * chore: update error handling for attachment download failure in message creation * chore: optimize update_contact_info method to use compact hash for updates * chore: simplify find_or_create_sender_contact method return values * chore: rename group and individual contact message handlers * chore: remove pointless comments from group contact message handler methods * chore: refine sender JID extraction logic to remove unnecessary checks * chore: remove phone number in spec for group contact attributes * chore: implement sync_group route * chore: implement get group_members route * fix: sync_group participants creation handling * chore: update contact avatar handling in group message processing * chore: move sync_group functionality for conversation model * feat: add sync_group action to ConversationsController and route * fix: set contact name to phone in group message processing * chore: refine group member retrieval logic in sync_group service and view * feat: implement group participants update handling * feat: implement group updates handling and localization for group activities * chore: add handling for group membership requests and icon changes * chore: add authorization for sync_group action in ContactsController * chore: add sync_group endpoint specs for contact management * chore: add authorization for sync_group action in ConversationsController * chore: add specs for sync_group endpoint in ConversationsController * chore: refactor index action in GroupMembersController for improved conversation filtering * chore: add request specs for group_members endpoint in ContactsController * chore: add specs for sync_group method in Conversation model * chore: add specs for sync_group method in Channel::Whatsapp model * chore: remove comments in find_or_create_group_conversation method * chore: add specs for Contacts::SyncGroupService to validate group contact behavior * chore: add specs for Whatsapp::BaileysHandlers::GroupsUpdate to validate group updates * chore: add specs for Whatsapp::BaileysHandlers::GroupParticipantsUpdate to handle group participant actions * chore: add fallback for identifier when contact has no phone_number in SendOnWhatsappService * chore: add specs for group membership request and icon change handling in MessagesUpsert * chore: add specs for sync_group method to handle group metadata and participant updates * chore: update sync_group method to retrieve group members and adjust JSON response * chore: update conversation query to filter by group type in GroupMembersController * chore: update conversation creation in group_members_controller_spec to specify conversation_type as group * chore: update find_or_create_group_conversation to include pending conversations * chore: refactor sync_group method and enhance specs for group conversation handling * feat: add GroupEventHelper module for managing group activities and contacts * chore: refactor group contact inbox and conversation creation methods in group handlers * chore: remove unnecessary check for blank participant contacts in sync_group_members method * chore: update swagger with endpoints for syncing group information and listing group members * chore: integrate Contacts::SyncGroupService in group members controller, enhance error handling and update swagger * fix: update recipient_id logic to directly use contact identifier for group contacts * chore: implement group stub message handling for membership requests and icon changes * fix: update whatsapp inbox source_id validation regex spec * chore: fix spec for contact syncing group * fix: optimize update_last_seen_at method to use update_columns * feat: Implement full frontend and backend support for group conversations - Added PRD for group conversations detailing frontend and backend requirements. - Created new Baileys TypeScript definitions for group-related functions. - Renamed `conversation_type` to `group_type` in the database and updated all references. - Implemented API serialization for `group_type` in conversation and contact responses. - Developed Vuex store module for managing group members. - Created UI components for group management, including group creation, member management, and metadata editing. - Integrated @mention functionality for group conversations and real-time updates via ActionCable. * feat: [US-001] - Rename conversation_type to group_type on conversations - Add migration to rename column and indexes - Update Conversation model enum to group_type - Update GroupConversationHandler concern - Update controllers (contacts, group_members) - Update all backend specs * chore: mark US-001 complete, update progress log, fix rubocop annotation * feat: [US-002] - Serialize group_type fields in API responses * feat: [US-003] - Add group_type filter to conversations index * feat: [US-004] - Add group_type to filter_keys.yml and FilterService * feat: US-005 - Backend group creation endpoint - Add POST /api/v1/accounts/:account_id/groups endpoint - Add Groups::CreateService to orchestrate Baileys group creation - Extend WhatsappBaileysService and BaseService with group management methods - Add routes for group members, metadata, invite, and join requests - Returns 403 when agent lacks inbox access, 422 when provider is unavailable * feat: US-006 - Backend add/remove members and role management endpoints - Add create/destroy/update actions to GroupMembersController - Delegate group management methods from Channel::Whatsapp to provider_service - create adds members via Baileys and creates ConversationGroupMember records - destroy removes a member by ID and sets is_active false - update promotes/demotes a member and updates their role * feat: US-007 - Backend group metadata update endpoint - Add PATCH /contacts/:id/group_metadata endpoint - Updates group subject via Baileys and syncs contact name - Updates group description via Baileys and syncs additional_attributes.description - Returns 422 when provider is unavailable * feat: US-008 - Backend invite link management endpoints - Add GET /contacts/:id/group_invite to retrieve current invite code/url - Add POST /contacts/:id/group_invite/revoke to revoke and get new invite code/url - Returns 422 when provider is unavailable * feat: US-009 - Backend join request management endpoints - Add GET /contacts/:id/group_join_requests to list pending join requests - Add POST /contacts/:id/group_join_requests/handle to approve/reject requests - Uses request_action param to avoid conflict with Rails reserved params[:action] - Returns 422 when provider is unavailable * feat: US-010 - Extend MentionService for contact mentions - Extract mention://contact/ID/Name URIs from message content - Store mentioned contact IDs in message.content_attributes[mentioned_contacts] - Existing user/team mention handling unchanged * feat: US-011 - Frontend API clients for all group endpoints - Add app/javascript/dashboard/api/groupMembers.js - Exports 11 methods: getGroupMembers, syncGroup, createGroup, updateGroupMetadata, addMembers, removeMembers, updateMemberRole, getInviteLink, revokeInviteLink, getPendingRequests, handleJoinRequest * feat: US-012 - Frontend Vuex store module groupMembers - Add groupMembers store module with fetch, sync, addMembers, removeMembers, updateMemberRole actions - Add SET_GROUP_MEMBERS and SET_GROUP_MEMBERS_UI_FLAG mutation types - Register module in store index * feat: US-013 - Frontend i18n keys for group features - Add groups.json with keys for group info, filter, creation modal, metadata editing, invite link, member management, join requests, and mention dropdown - Register groups.json in i18n locale en/index.js Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: US-014 - Frontend group_type filter in ConversationBasicFilter - Add chatGroupTypeFilter state, getter, mutation, and action to conversations store - Add getChatGroupTypeFilter getter - Add group_type param to ConversationApi.get() - Add Type filter section to ConversationBasicFilter with All/Individual/Group options - Persist group_type to UI settings under conversations_filter_by.group_type - Restore group_type from UI settings on page load - Include groupType in conversationFilters and pass as group_type param to API Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: US-013 - Frontend — i18n keys for group features (en + pt-BR) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: [US-014] - Frontend — add group_type filter to ConversationBasicFilter All implementation was already in place from prior work: - ConversationBasicFilter.vue has Type section with All/Individual/Group options - ChatList.vue handles group_type in conversationFilters and restores from UI settings - Store has setChatGroupTypeFilter action, getChatGroupTypeFilter getter - API maps groupType → group_type query param Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-015 - Frontend — add group_type to advanced filter system - Add GROUP_TYPE to CONVERSATION_ATTRIBUTES in filterHelper.js - Add group_type filter definition in provider.js (components-next) - Add group_type to legacy advancedFilterItems/index.js and filterAttributeGroups - Add group_type to automationHelper conditionFilterMaps - Add group_type to customViewsHelper getValuesForFilter - Add group_type options to ChatList setParamsForEditFolderModal - Add GROUP_TYPE i18n key in en and pt_BR advancedFilters.json Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-016 - Frontend — GroupContactInfo basic display Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-017 - Frontend — GroupContactInfo sync button Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-018 - integrate GroupContactInfo in ContactPanel - Import GroupContactInfo component - Conditionally render GroupContactInfo when group_type === 'group' - Keep ContactInfo for individual conversations (no regression) - Dynamic sidebar title: 'Group' for groups, 'Contact' for individual - contact_notes and contact_attributes accordion sections unchanged Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-019 - Frontend — group creation UI modal Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-020 - Frontend — member management UI in GroupContactInfo Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-021 - Frontend — group metadata editing UI Add inline editing for group name, description, and avatar in GroupContactInfo: - Click group name to edit inline, save on Enter/blur - Click description to edit inline with textarea, save on blur - Click avatar to open file picker for upload via contacts/update - Loading states on all fields during save - Success/error alerts for all operations - updateGroupMetadata action added to groupMembers store Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-022 - Frontend — invite link management UI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-023 - Frontend — join request management UI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-024 - Frontend — group message bubbles: sender name with color - Add sender name display above incoming message bubbles in group conversations - Deterministic color per sender using AVATAR_COLORS palette (name.length % 6) - Sender name hidden for consecutive messages from the same sender - Individual conversation bubbles unchanged - Pass groupWithPrevious and isGroupConversation props through MessageList → Message Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-025 - Frontend — group message bubbles: sender avatar Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: Add Ralph Wiggum AI agent script for managing tool execution and progress tracking * feat: US-026 - Frontend — @mention dropdown for group conversations - Create TagGroupMembers.vue component for group member mention suggestions - Modify Editor.vue: add isGroupConversation/groupContactId props, render TagGroupMembers for group non-private context - Modify ReplyBox.vue: compute isGroupConversation and groupContactId from currentChat, pass to WootMessageEditor - @ mention plugin isAllowed now triggers for group conversations - In individual conversations or private notes, existing behavior unchanged Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-027 - Frontend — mention rendering in group message bubbles Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-028 - Frontend ActionCable handler for contact.group_synced event - Backend: Include group_members data in contact.group_synced ActionCable payload - Frontend: Register contact.group_synced handler in ActionCableConnector - Frontend: Add setGroupMembers action to groupMembers store for direct commits - Tests: ActionCable handler spec + groupMembers store spec for new action Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: Update progress tracking for group conversations feature - mark tests as passing * fix: sender click case mismatch and filter dropdown spacing - Message.vue: use case-insensitive comparison for sender type check (Contact.push_event_data returns 'contact' but SENDER_TYPES.CONTACT is 'Contact') - ConversationBasicFilter.vue: replace last:mt-4 with flex-col gap-4 for consistent spacing between all three filter sections * fix: four bugs found during manual testing review - ContactPanel.vue: fix i18n key GROUP.INFO.SIDEBAR_TITLE → GROUP.SIDEBAR_TITLE - groupMembers.js API: fix syncGroup HTTP method GET → POST to match backend route - group_members_controller.rb: remove SyncGroupService from index action - filterHelpers.js: add missing group_type case to getValueFromConversation * docs: update progress with bug fix learnings * chore: implement group creation functionality in UI components * chore: add copy invite link functionality and update UI components * feat: US-041 - Backend — ensure group_type is set on existing contacts and conversations GroupConversationHandler#update_group_contact_info now sets group_type: :group on contacts that are incorrectly typed as individual. GroupConversationHandler#find_or_create_group_conversation updates existing conversation's group_type to :group if it is currently :individual. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: mark US-041 as complete Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-029 - i18n keys for You badge and group settings (en + pt-BR) All i18n keys already existed from prior iterations. Verified presence and updated PRD status. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-030 - fix Baileys API route/method mismatches Fix on_whatsapp to dig('data') before accessing first element. Update spec stubs to match { data: [...] } response envelope. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-031 - group_leave, group_setting_update, group_join_approval_mode methods All methods, delegates, and error handling already implemented. Verified specs pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-032 - persist group settings, invite code, and profile picture during sync Add try_update_group_avatar to fetch and attach group profile picture during sync_group. Update spec stubs for profile-picture-url endpoint. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-033 - GroupSettingsController with leave, update, toggle Controller and routes already implemented. Verified rubocop passes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-034 - remove inbox_contact_id from provider_config and jbuilder Already removed in prior iterations. Verified no references remain. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-035 - refactor TagGroupMembers to phone_number matching Already implemented. Verified excludePhoneNumber prop and filtering. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-036 - remove InboxContact.vue and settings tab Already removed in prior iterations. Verified no references remain. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-037 - add You badge in GroupContactInfo member list Already implemented with isOwnMember check and blue badge styling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-038 - fix inline edit for group name and description Already implemented with phone number normalization. Verified code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-039 - group settings section UI with toggles Already implemented. Settings toggles, API calls, and i18n verified. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-040 - leave group UI with confirmation and auto-resolve Already implemented. Leave button, confirmation, and API call verified. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-050 - Create GroupMember model and migration New group_members table with group_contact_id, contact_id, role, is_active. Unique index on (group_contact_id, contact_id). Associations added to Contact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-064 - Helper method to find channel from group contact Add Contact#group_channel to decouple channel lookup from conversations. Update GroupMembersController and GroupSettingsController to use it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-052 - Update GroupConversationHandler to use GroupMember Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-057 - Update GroupMembersController to query GroupMember Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-058 - Update GroupSettingsController to not depend on conversations Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-060 - Update group_members jbuilder views Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-059 - Remove group_members association from Conversation model Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: US-051 - Remove ConversationGroupMember model and table Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: mark all stories complete, update progress Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(groups): real-time group panel, avatar refresh on icon change, editable name/description - Add group_type to Conversations::EventDataPresenter#push_data and Contact#push_event_data so WebSocket events carry the field, enabling the frontend to switch to GroupContactInfo in real-time - Update handle_icon_change_stub to call try_update_group_avatar with force: true, purging the cached avatar and fetching the new one - Add force parameter to try_update_group_avatar to support re-fetching - Remove isInboxAdmin gate from name/description editing in GroupContactInfo so any user can click to edit (server validates) * fix(groups): rewrite SyncGroupService and simplify group metadata channel lookup - Rewrite SyncGroupService to use contact.group_channel directly instead of iterating conversations; find or create a conversation for sync - Simplify GroupMetadataController to use @contact.group_channel instead of querying conversations; remove local contact/attribute updates since the Baileys API handles persistence via webhook events * feat(groups): resolve conversations when inbox phone leaves or is removed - Add resolve_conversations_if_inbox_left to GroupParticipantsUpdate - Resolves all open/pending conversations when the inbox phone number is removed from or leaves a group * feat(groups): add paginated member list with infinite scroll - Backend: add pagination to GroupMembersController (page/per_page, default 10, ordered admins first); add meta with pagination info to the jbuilder response - Frontend: update groupMembers API to accept page param; add APPEND_GROUP_MEMBERS and SET_GROUP_MEMBERS_META mutations; implement paginated fetch with append and isFetchingMore flag in store * feat(groups): support Ctrl+Click on group message sender to open in new tab - navigateToGroupSender now accepts the event and checks for Ctrl/Cmd+Click to open the sender contact in a new tab * chore(i18n): update leave group confirmation text in en and pt_BR * fix(groups): handle phone format differences in You badge and admin detection - Extract phonesMatch helper that compares last 8 digits as fallback, handling Brazilian 9th digit discrepancy (e.g. +5587988465072 vs +558788465072) - Apply to both isOwnMember and isInboxAdmin computed properties * feat(groups): auto-sync members on mount, show existing members immediately - On mount, fetch existing DB members first so they display instantly - Then silently attempt a background sync to refresh from WhatsApp - If sync fails (e.g. WhatsApp disconnected), existing members remain displayed without any user-facing error * fix(groups): pin own member on first page and return inbox phone in meta The "You" badge was not appearing because the inbox's own member could be missing from the first paginated page in large groups (admins sorted first). Backend: - Pin the inbox's own member at the top of page 1 regardless of sort order - Return inbox_phone_number in the group members meta response - Use last-8-digit SQL fallback for Brazilian 9th-digit phone mismatches Frontend: - Use meta.inbox_phone_number for the inboxPhone computed - Fix declaration order to satisfy no-use-before-define lint rule * fix(groups): fix member action dropdown clipped by overflow container The promote/demote/remove dropdown menu was invisible because the member list had `overflow-y-auto max-h-80`, clipping any absolutely-positioned dropdown rendered inside it. - Remove overflow container from member list; let the sidebar scroll - Replace scroll-based infinite loading with IntersectionObserver on a sentinel element for cleaner pagination trigger - All member action logic (promote, demote, remove) was already wired; the dropdown is now visible on hover * fix(groups): keep member action dropdown visible when menu is open The opacity-0/group-hover classes on the action menu wrapper caused the DropdownMenu to become invisible as soon as the mouse left the row. Now the wrapper stays fully opaque while the menu is active. * fix(groups): move clickaway to member list wrapper to prevent instant close v-on-clickaway was bound to every member's action div individually. Clicking the three-dot button on one member fired closeMemberMenu from all other members' clickaway handlers, closing the menu instantly. Moved the directive to the single member list container instead. * feat: add WhatsApp mention conversion (incoming + outgoing) - New MentionConverterService for bidirectional mention handling - Incoming: converts @phone/mentionedJid to mention://contact/ URIs - Outgoing: extracts mention://contact/ URIs into WhatsApp mentions array - Supports @everyone/todos group mentions - WhatsApp renderer preserves mention display text instead of raw URI * fix: preserve mention display text in WhatsApp renderer mention:// URIs now render as display name text instead of the raw URL when converting markdown to WhatsApp format * feat: add @everyone mention option in group conversations - Everyone item shown at top of mention dropdown - Searchable by 'all', 'todos', 'everyone' keywords - i18n keys added for en and pt-BR * refactor: use Switch component for group settings toggles - Add disabled prop to Switch component - Replace custom toggle buttons in GroupContactInfo with Switch - Loading spinner shown alongside toggle while toggling * feat(whatsapp): add group sync status tracking (group_left, group_last_synced_at) * feat(whatsapp): hide group management UI when group_left is true * fix(groupMembers): include inbox phone number in group members state and sync event * feat(whatsapp): wrap group settings and leave in Accordion component * feat(groupMembers): handle group creator modification errors and update error messages * feat(groupMembers): enhance invite link functionality and clean up UI state after copying * refactor: remove sync_group functionality from conversations and related specs * feat(GroupContactInfo): implement scroll-based loading for group members * docs(swagger): add group API endpoints and remove conversation sync_group - Remove dead conversation/{id}/sync_group swagger entry and file - Update group_members.yml with pagination params, POST operation, and $ref schema - Add swagger for: group_members_member (PATCH/DELETE), group_metadata, group_invite, group_invite_revoke, group_join_requests, group_join_requests_handle, group_settings, group_settings_leave, group_settings_toggle_join_approval, groups/create - Add group_member schema definition - Add Groups tag to application tag_groups - Register all 12 group endpoints in paths/index.yml * feat(WhatsappBaileysService): enhance mention handling by replacing @DisplayName with @lid/@phone in outgoing text * feat(groups): move group sync to background job with 15-min cooldown - Create Contacts::SyncGroupJob that checks group_last_synced_at before calling SyncGroupService (skips if < 15 min) - Controller sync_group now enqueues the job and returns 202 Accepted - Delete sync_group.json.jbuilder (no longer needed) - Frontend sync action is fire-and-forget; results via ActionCable - Auto-trigger sync on conversation select and panel mount - Remove manual sync button from GroupContactInfo * fix: show group members list even after leaving group\n\nKeep the members section visible in read-only mode when\ngroup_left is true. Admin actions (add member, promote,\ndemote, remove) remain hidden. Pending Join Requests and\nAdvanced Options also stay hidden. * fix: disable group name/description/avatar editing when group_left is true * fix: remove @all mention and fix Enter key in group mention dropdown\n\n- Remove the @all/everyone special mention from TagGroupMembers since\n no channel provider currently supports mentioning all participants\n- Fix Enter key sending message instead of inserting selected mention\n in group conversations. The root cause was Editor.vue only emitting\n toggleUserMention=true for private notes (isPrivate), leaving\n ReplyBox unaware the group mention dropdown was open. Now also\n emits for isGroupConversation.\n- Add TagGroupMembers spec covering filtering, exclusion, and emission" * fix: address PR review feedback for group conversations - Fix nil safety in group_invites and group_join_requests controllers by replacing group_conversation.inbox.channel with @contact.group_channel - Add before_action guard in group_members_controller to validate contact is a group with identifier before create/update/destroy - Persist metadata locally in group_metadata_controller after provider calls (subject -> name, description -> additional_attributes) - Add server-side allow_group_creation? check in groups_controller - Add word boundary to mention regex to prevent matching inside words - Remove useless catch clauses in groupMembers store (try/finally only) - Default groupType to [] in customViewsHelper to prevent crash - Fix swagger parameter name mismatch (contact_id -> id) across all group endpoint YML files for consistency * fix: address PR #228 review feedback - strong params, guards, and safety fixes * fix: dispatch real-time events for Baileys group participant and metadata updates Both group-participants.update and groups.update handlers were updating backend data (GroupMember records, Contact attributes) but never dispatching ActionCable events, leaving the frontend member list and group metadata stale until manual sync. Changes: - Add dispatch_group_synced_event helper to GroupEventHelper concern - Dispatch CONTACT_GROUP_SYNCED after participant add/remove/promote/demote - Dispatch CONTACT_GROUP_SYNCED after group subject/description/settings changes - Frontend: onContactGroupSynced also dispatches contacts/updateContact to refresh group name, description and settings in the sidebar * fix: enhance member menu positioning and close behavior on sidebar scroll * feat: implement group property updates and enhance toast notifications * fix: update WhatsApp channel regex to allow optional hyphenated numbers * feat: implement group admin functionalities including leave, update properties, and toggle join approval * refactor: simplify group message handling by removing metadata fetching and syncing methods * chore: remove raph files * feat: update Portuguese translations for 'Read More' and 'Insert Read More' phrases * feat: enhance group admin functionalities with join approval and member add modes * feat: enhance group join request handling by adding removal of handled requests and updating pending join requests * feat: restrict message sending in announcement mode groups When a Baileys WhatsApp group has announcement mode enabled (announce=true), only admin members can send messages. This adds: - Frontend: disabled editor + banner for non-admin inbox in announcement groups - Backend: validation in SendOnWhatsappService to reject messages - Shared phone helper utility extracted from GroupContactInfo - i18n keys for en and pt_BR * feat: add group sync job enqueueing and improve avatar update handling * feat: add functionality to reset invite link and confirm member addition restrictions * feat: update group name extraction logic to handle nil values * feat: add inbox admin status handling and update related components * feat: remove group conversation resolution on leave action * feat: enhance group sender avatar interaction with tooltip and cursor pointer * feat: add force option to SyncGroupJob and update related specs * feat: enhance invite link handling and avatar update logic in group conversations * chore: remove prd.json * fix: change group sender name display from block to inline-block for better layout * feat: add group members loading check and fetch logic in MessagesView and ReplyBox components * feat: allow id and firstUnreadId props to accept both Number and String types feat: add vOnClickOutside import to Editor component feat: enhance Portuguese translations for integrations and settings fix: change button color in GroupContactInfo component from green to teal * feat: soft-disabled group conversations with activity tracking Groups start in a soft-disabled state by default when using Baileys. Chatwoot still creates group conversations but does not process every incoming message. Instead, Baileys accumulates group messages and sends periodic groups.activity webhook events to update last_activity_at. Backend: - Add WHATSAPP_GROUPS_ENABLED env var and groups_enabled? class method - Send groupsEnabled in Baileys connection setup - Create groups.activity handler to update conversation last_activity_at - Gate group message processing behind groups_enabled? check - Expose groups_enabled via inbox API Frontend: - Add warning banner with CTA to app.fazer.ai on disabled group conversations - Disable reply editor for non-private-note mode when groups disabled - Add i18n strings for en and pt_BR Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use method for groups disabled banner action to avoid window scope issue Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: broadcast conversation update after groups.activity event update_columns bypasses ActiveRecord callbacks, so the ActionCable broadcast was never triggered when last_activity_at changed. Dispatch a CONVERSATION_UPDATED event explicitly so the sidebar updates in real-time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: show unread dot for soft-disabled group conversations with activity Since soft-disabled groups don't create messages, unread_count is always 0 and the standard badge won't show. Detect unread state by comparing last_activity_at > agent_last_seen_at for these groups and display a teal dot indicator instead of a count badge. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: clear unread dot when agent opens soft-disabled group conversation The update_last_seen endpoint skipped updating agent_last_seen_at when there were no unread messages (the throttle path). For soft-disabled groups that never create messages, this meant the dot indicator could never be cleared. Add an unseen_activity? check that bypasses the throttle when last_activity_at > agent_last_seen_at. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: group avatar upload to provider and fix icon change sync - Route avatar upload through GroupMetadataController to push to WhatsApp provider before saving locally - Add update_group_picture to baileys service and base service - Fix buildContactFormData crash when social_profiles is undefined - Make try_update_group_avatar public so GROUP_CHANGE_ICON stub handler can call it from outside the service class Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: update specs for group conversations feature changes - Add groupsEnabled param to setup_channel_provider and handle_channel_error WebMock stubs - Add group-request-participants-list stub for sync_group tests - Add group_type to push_event_data expected hash - Set last_activity_at in throttle tests to prevent unseen_activity? bypass - Update sync_group delegation expectation to include soft: false - Stub groups_enabled? in group message handling tests - Update WhatsApp source_id regex expectation for group contact IDs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: add settings file for additional directories configuration * chore: undo unrelated changes * chore: remove planning doc, fix migration version, fix swagger param consistency - Remove planejamento-chat-interno.md (unrelated planning document) - Fix CreateGroupMembers migration API version from 7.0 to 7.1 - Fix swagger.json: normalize group endpoint paths from {contact_id} to {id} to match YAML sources and existing contact sub-resource conventions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: CayoPOliveira <cayoproliveira@gmail.com> Co-authored-by: Cayo P. R. Oliveira <cayo@fazer.ai> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
05e0d355dd
|
fix(captain): prevent clamp ArgumentError when captain limits are negative (#242)
When creating an inbox, `usage_limits` eagerly computes captain limits even though only the `:inboxes` value is needed. If `total_count` in `get_captain_limits` resolves to a negative number, `clamp(0, negative)` raises `ArgumentError: min argument must be less than or equal to max argument`, causing a 500 on inbox creation. Ensure `total_count` is floored at 0 so the clamp range is always valid. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
ac93290c9a
|
fix: skip captain auto-open for templates (#13802)
# Pull Request Template ## Description WhatsApp template messages are treated as outgoing human messages so conversations are automatically marked Open, preventing Captain from responding. #13673 This PR fixes the behaviour for template messages, so that captain can respond to them. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules |
||
|
|
d6d38cdd7d
|
feat: captain decides if conversation should be resolved or kept open (#13336)
# Pull Request Template ## Description captain decides if conversation should be resolved or open Fixes https://linear.app/chatwoot/issue/AI-91/make-captain-resolution-time-configurable Update: Added 2 entries in reporting events: `conversation_captain_handoff` and `conversation_captain_resolved` ## Type of change Please delete options that are not relevant. - [x] New feature (non-breaking change which adds functionality) - [x] This change requires a documentation update ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. LLM call decides that conversation is resolved, drops a private note <img width="1228" height="438" alt="image" src="https://github.com/user-attachments/assets/fb2cf1e9-4b2b-458b-a1e2-45c53d6a0158" /> LLM call decides conversation is still open as query was not resolved <img width="1215" height="573" alt="image" src="https://github.com/user-attachments/assets/2d1d5322-f567-487e-954e-11ab0798d11c" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sojan Jose <sojan@pepalo.com> |
||
|
|
dbe35252bc
|
fix: Use handoff_key for scenarios (#13755)
# Pull Request Template
## Description
Ensure agent function names stay within OpenAI's 64-char limit
(ai-agents prepends "handoff_to_").
Add HANDOFF_TITLE_SLUG_MAX_LENGTH and
handoff_key generation: persisted records use `scenario_{id}_agent`; new
records use a truncated title slug.
Assistant scenario keys and agent_name now reference the generated
handoff key.
fixes :
`Invalid 'messages[9].tool_calls[0].function.name': string too long.
Expected a string with maximum length 64, but got a string with length
95 instead.`
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
Tested locally
<img width="1806" height="1044" alt="image"
src="https://github.com/user-attachments/assets/40cd7a3d-3d97-43a8-bd56-d3f5d63abbda"
/>
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
|
||
|
|
059506b1db
|
feat: Add automatic favicon fetching for companies (#13013)
## Summary This Enterprise-only feature automatically fetches a favicon for companies created with a domain, and adds a batch task to backfill missing avatars for existing companies. The flow only targets companies that do not already have an attached avatar, so existing avatars are left untouched. ## Demo https://github.com/user-attachments/assets/d050334e-769f-4e46-b6e7-f7423727a192 ## What changed - Added `Avatar::AvatarFromFaviconJob` to build a Google favicon URL from the company domain and fetch it through `Avatar::AvatarFromUrlJob` - Triggered favicon fetching from `Company` with `after_create_commit` - Added `Companies::FetchAvatarsJob` to batch existing companies that are missing avatars - Added `companies:fetch_missing_avatars` under `enterprise/lib/tasks` - Kept the company-specific implementation inside the Enterprise boundary - Stubbed the new favicon request in unrelated specs that now hit this callback indirectly - Updated a couple of CI-sensitive specs that were failing due to callback side effects / reload-safe exception assertions ## How to verify 1. Create a company in Enterprise with a valid domain and no avatar. 2. Confirm that a favicon-based avatar gets attached shortly after creation. 3. Create another company with a domain and an avatar already attached. 4. Confirm that the existing avatar is not replaced. 5. Run `companies:fetch_missing_avatars`. 6. Confirm that existing companies without avatars get one, while companies that already have avatars remain unchanged. ## Notes - This change does not refresh or overwrite existing company avatars - Favicon fetching only runs for companies with a present domain - The branch includes the latest `develop` --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Sojan Jose <sojan@pepalo.com> |
||
|
|
374d2258c7
|
fix: captain talking over support agent (#13673) | ||
|
|
c218eff5ec
|
feat: add per-webhook secret with backfill migration (#13573) | ||
|
|
7c60ad9e28
|
feat: include contact verified status with each tool call (#13663)
Co-authored-by: aakashb95 <aakashbakhle@gmail.com> |
||
|
|
b98c614669
|
feat: add campaign context to Captain v2 prompts (#13644)
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com> |
||
|
|
d8f4bb940e
|
feat: add resolve_conversation tool for Captain V2 scenarios (#13597)
# Pull Request Template ## Description Adds a new built-in tool that allows Captain scenarios to resolve conversations programmatically. This enables automated workflows like the misdirected contact deflector to close conversations after handling them, while still allowing human review via label filtering. ## Type of change Please delete options that are not relevant. - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. tested by mentioning it to be used in captain v2 scenario <img width="1180" height="828" alt="image" src="https://github.com/user-attachments/assets/e70baf96-0c70-407e-af2c-328500ac5434" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com> |
||
|
|
9a4c5058f3 | Merge branch 'main' into chore/merge-upstream-4.11.0 | ||
|
|
00ed074d72
|
fix: disable email transcript for free plans (#13509)
- Block email transcript functionality for accounts without a paid plan to prevent SES abuse. |
||
|
|
7b512bd00e
|
fix: V2 Assignment service enhancements (#13036)
## Linear Ticket: https://linear.app/chatwoot/issue/CW-6081/review-feedback ## Description Assignment V2 Service Enhancements - Enable Assignment V2 on plan upgrade - Fix UI issue with fair distribution policy display - Add advanced assignment feature flag and enhance Assignment V2 capabilities ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? This has been tested using the UI. ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes auto-assignment execution paths, rate limiting defaults, and feature-flag gating (including premium plan behavior), which could affect which conversations get assigned and when. UI rewires inbox settings and policy flows, so regressions are possible around navigation/linking and feature visibility. > > **Overview** > **Adds a new premium `advanced_assignment` feature flag** and uses it to gate capacity/balanced assignment features in the UI (sidebar entry, settings routes, assignment-policy landing cards) and backend (Enterprise balanced selector + capacity filtering). `advanced_assignment` is marked premium, included in Business plan entitlements, and auto-synced in Enterprise accounts when `assignment_v2` is toggled. > > **Improves Assignment V2 policy UX** by adding an inbox-level “Conversation Assignment” section (behind `assignment_v2`) that can link/unlink an assignment policy, navigate to create/edit policy flows with `inboxId` query context, and show an inbox-link prompt after creating a policy. The policy form now defaults to enabled, disables the `balanced` option with a premium badge/message when unavailable, and inbox lists support click-to-navigate. > > **Tightens/adjusts auto-assignment behavior**: bulk assignment now requires `inbox.enable_auto_assignment?`, conversation ordering uses the attached `assignment_policy` priority, and rate limiting uses `assignment_policy` config with an infinite default limit while still tracking assignments. Tests and i18n strings are updated accordingly. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 23bc03bf75ee4376071e4d7fc7cd564c601d33d7. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Pranav <pranav@chatwoot.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> |
||
|
|
c884cdefde
|
feat: add per-account daily rate limit for outbound emails (#13411)
Introduce a daily cap on non-channel outbound emails to prevent abuse. Fixes https://linear.app/chatwoot/issue/CW-6418/ses-incident-jan-28 ## Type of change - [x] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality not to work as expected) ## Summary - Adds a Redis-based daily counter to rate limit outbound emails per account, preventing email abuse - Covers continuity emails (WebWidget/API), conversation transcripts, and agent notifications - Email channel replies are excluded (paid feature, not abusable) - Adds account suspension check in `ConversationReplyMailer` to block already-queued emails for suspended accounts ## Limit Resolution Hierarchy 1. Per-account override (`account.limits['emails']`) — SuperAdmin configurable 2. Enterprise plan-based (`ACCOUNT_EMAILS_PLAN_LIMITS` InstallationConfig) 3. Global default (`ACCOUNT_EMAILS_LIMIT` InstallationConfig, default: 100) 4. Fallback (`ChatwootApp.max_limit` — effectively unlimited) ## Enforcement Points | Path | Where | Behavior | |------|-------|----------| | WebWidget/API continuity | `SendEmailNotificationService#should_send_email_notification?` | Silently skipped | | Widget transcript | `Widget::ConversationsController#transcript` | Returns 429 | | API transcript | `ConversationsController#transcript` | Returns 429 | | Agent notifications | `Notification::EmailNotificationService#perform` | Silently skipped | | Email channel replies | Not rate limited | Paid feature | | Suspended accounts | `ConversationReplyMailer` | Blocked at mailer level | |
||
|
|
04b2901e1f
|
feat: Conversation workflows(EE) (#13040)
We are expanding Chatwoot’s automation capabilities by introducing **Conversation Workflows**, a dedicated section in settings where teams can configure rules that govern how conversations are closed and what information agents must fill before resolving. This feature helps teams enforce data consistency, collect structured resolution information, and ensure downstream reporting is accurate. Instead of having auto‑resolution buried inside Account Settings, we introduced a new sidebar item: - Auto‑resolve conversations (existing behaviour) - Required attributes on resolution (new) This groups all conversation‑closing logic into a single place. #### Required Attributes on Resolve Admins can now pick which custom conversation attributes must be filled before an agent can resolve a conversation. **How it works** - Admin selects one or more attributes from the list of existing conversation level custom attributes. - These selected attributes become mandatory during resolution. - List all the attributes configured via Required Attributes (Text, Number, Link, Date, List, Checkbox) - When an agent clicks Resolve Conversation: If attributes already have values → the conversation resolves normally. If attributes are missing → a modal appears prompting the agent to fill them. <img width="1554" height="1282" alt="CleanShot 2025-12-10 at 11 42 23@2x" src="https://github.com/user-attachments/assets/4cd5d6e1-abe8-4999-accd-d4a08913b373" /> #### Custom Attributes Integration On the Custom Attributes page, we will surfaced indicators showing how each attribute is being used. Each attribute will show badges such as: - Resolution → used in the required‑on‑resolve workflow - Pre‑chat form → already existing <img width="2390" height="1822" alt="CleanShot 2025-12-10 at 11 43 42@2x" src="https://github.com/user-attachments/assets/b92a6eb7-7f6c-40e6-bf23-6a5310f2d9c5" /> #### Admin Flow - Navigate to Settings → Conversation Workflows. - Under Required attributes on resolve, click Add Required Attribute. - Pick from the dropdown list of conversation attributes. - Save changes. Agents will now be prompted automatically whenever they resolve. <img width="2434" height="872" alt="CleanShot 2025-12-10 at 11 44 42@2x" src="https://github.com/user-attachments/assets/632fc0e5-767c-4a1c-8cf4-ffe3d058d319" /> #### NOTES - The Required Attributes on Resolve modal should only appear when values are missing. - Required attributes must block the resolution action until satisfied. - Bulk‑resolve actions should follow the same rules — any conversation missing attributes cannot be bulk‑resolved, rest will be resolved, show a notification that the resolution cannot be done. - API resolution does not respect the attributes. --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com> |
||
|
|
6ab1898992 | Merge branch 'main' into chore/merge-upstream-4.10 | ||
|
|
005a22fd69
|
feat: Add sorting by contacts count to companies list (#13012)
## Description Adds the ability to sort companies by the number of contacts they have (contacts_count) in ascending or descending order. This is part of the Chatwoot 5.0 release requirements for the companies feature. The implementation uses a scope-based approach consistent with other sorting implementations in the codebase (e.g., contacts sorting by last_activity_at). ## Type of change - [x] New feature (non-breaking change which adds functionality) ## Available Sorting Options After this change, the Companies API supports the following sorting options: | Sort Field | Type | Ascending | Descending | |------------|------|-----------|------------| | `name` | string | `?sort=name` | `?sort=-name` | | `domain` | string | `?sort=domain` | `?sort=-domain` | | `created_at` | datetime | `?sort=created_at` | `?sort=-created_at` | | `contacts_count` | integer (scope) | `?sort=contacts_count` | `?sort=-contacts_count` | **Note:** Prefix with `-` for descending order. Companies with NULL contacts_count will appear last (NULLS LAST). ## CURL Examples **Sort by contacts count (ascending):** ```bash curl -X GET 'https://app.chatwoot.com/api/v1/accounts/{account_id}/companies?sort=contacts_count' \ -H 'api_access_token: YOUR_API_TOKEN' ``` **Sort by contacts count (descending):** ```bash curl -X GET 'https://app.chatwoot.com/api/v1/accounts/{account_id}/companies?sort=-contacts_count' \ -H 'api_access_token: YOUR_API_TOKEN' ``` **Sort by name (ascending):** ```bash curl -X GET 'https://app.chatwoot.com/api/v1/accounts/{account_id}/companies?sort=name' \ -H 'api_access_token: YOUR_API_TOKEN' ``` **Sort by created_at (descending):** ```bash curl -X GET 'https://app.chatwoot.com/api/v1/accounts/{account_id}/companies?sort=-created_at' \ -H 'api_access_token: YOUR_API_TOKEN' ``` **With pagination:** ```bash curl -X GET 'https://app.chatwoot.com/api/v1/accounts/{account_id}/companies?sort=-contacts_count&page=2' \ -H 'api_access_token: YOUR_API_TOKEN' ``` ## How Has This Been Tested? - Added RSpec tests for both ascending and descending sort - All 24 existing specs pass - Manually tested the sorting functionality with test data **Test configuration:** - Ruby 3.4.4 - Rails 7.1.5.2 - PostgreSQL (test database) **To reproduce:** 1. Run `bundle exec rspec spec/enterprise/controllers/api/v1/accounts/companies_controller_spec.rb` 2. All tests should pass (24 examples, 0 failures) ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes ## Technical Details **Backend changes:** - Controller: Added `sort_on :contacts_count` with scope-based sorting - Model: Added `order_on_contacts_count` scope using `Arel::Nodes::SqlLiteral` and `sanitize_sql_for_order` with `NULLS LAST` for consistent NULL handling - Specs: Added 2 new tests for ascending/descending sort validation **Files changed:** - `enterprise/app/controllers/api/v1/accounts/companies_controller.rb` - `enterprise/app/models/company.rb` - `spec/enterprise/controllers/api/v1/accounts/companies_controller_spec.rb` **Note:** This PR only includes the backend implementation. Frontend changes (sort menu UI + i18n) will follow in a separate commit. --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Pranav <pranav@chatwoot.com> |
||
|
|
549214e96d | Merge branch main into chore/merge-upstream | ||
|
|
d2ba9a2ad3
|
feat(enterprise): add voice conference API (#13064)
The backend APIs for the voice call channel ref: #11602 |
||
|
|
1de8d3e56d
|
feat: legacy features to ruby llm (#12994) | ||
|
|
0d8e249fe4
|
feat: include chatwoot metadata with each tool call (#12907) | ||
|
|
20fa5eeaa5
|
fix: Prevent SLA deletion timeouts by moving to async job (#12944)
This PR fixes the HTTP 500 timeout errors occurring when deleting SLA
policies that have large volumes of historical data.
The fix moves the deletion workflow to asynchronous background
processing using the existing `DeleteObjectJob`.
By offloading heavy cascaded deletions (applied SLAs, SLA events,
conversation nullifications) from the request cycle, the API can now
return immediately while the cleanup continues in the background
avoiding the `Rack::Timeout::RequestTimeoutException`. This ensures that
SLA policies can be deleted reliably, regardless of data size.
### Problem
Deleting an SLA policy via `DELETE
/api/v1/accounts/{account_id}/sla_policies/{id}` fails consistently with
`Rack::Timeout::RequestTimeoutException (15s)` for policies with large
amounts of related data.
Because the current implementation performs all dependent deletions
**synchronously**, Rails processes:
- `has_many :applied_slas, dependent: :destroy` (thousands)
- Each `AppliedSla#destroy` → triggers destruction of many `SlaEvent`
records
- `has_many :conversations, dependent: :nullify` (thousands)
This processing far exceeds the Rack timeout window and consistently
triggers HTTP 500 errors for users.
### Solution
This PR applies the same pattern used successfully in Inbox deletion.
**Move deletion to async background jobs**
- Uses `DeleteObjectJob` for centralized, reliable cleanup.
- Allows the DELETE API call to respond immediately.
**Chunk large datasets**
- Records are processed in **batches of 5,000** to reduce DB load and
avoid job timeouts.
|
||
|
|
48627da0f9
|
feat: outbound voice call essentials (#12782)
- Enables outbound voice calls in voice channel . We are only caring about wiring the logic to trigger outgoing calls to the call button introduced in previous PRs. We will connect it to call component in subsequent PRs ref: #11602 ## Screens <img width="2304" height="1202" alt="image" src="https://github.com/user-attachments/assets/b91543a8-8d4e-4229-bd80-9727b42c7b0f" /> <img width="2304" height="1200" alt="image" src="https://github.com/user-attachments/assets/1a1dad2a-8cb2-4aa2-9702-c062416556a7" /> --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com> |
||
|
|
b03dfdb751
|
Chore/merge upstream 4.8.0 (#150)
* chore: Hide "Learn More" button in feature spotlight for self-hosted (#12675) * feat: single query for reporting event stats (#12664) This PR collapses multiple queries fetching stats from a single table to a single query ```sql SELECT user_id as user_id, COUNT(CASE WHEN name = 'conversation_resolved' THEN 1 END) as resolved_count, AVG(CASE WHEN name = 'conversation_resolved' THEN value END) as avg_resolution_time, AVG(CASE WHEN name = 'first_response' THEN value END) as avg_first_response_time, AVG(CASE WHEN name = 'reply_time' THEN value END) as avg_reply_time FROM "reporting_events" WHERE "reporting_events"."account_id" = <account_id> AND "reporting_events"."created_at" >= '2025-09-14 18:30:00' AND "reporting_events"."created_at" < '2025-10-14 18:29:59' GROUP BY "reporting_events"."user_id"; ``` ### Why this works? Here's why this optimization is faster based on PostgreSQL internals: - Single Table Scan vs Multiple Scans: Earlier we did 4 sequential scans (or 4 index scans) of the same data, with the same where clause, now in a single scan all 4 `CASE` expressions are evaluated in a single pass. - Shared Buffer Cache Efficiency: PostgreSQL's shared buffer cache stores recently accessed pages, with this, pages are loaded once and re-used for all aggregation, earlier with separate queries we were forced to re-read all from the disk each time - Reduced planning and network overhead (4 vs 1 query) ### How is it tested 1. The specs all pass without making any changes 2. Verified the reports side by side after generating from report seeder #### How to test Generate seed data using the following command ```bash ACCOUNT_ID=1 ENABLE_ACCOUNT_SEEDING=true bundle exec rake db:seed:reports_data ``` Once done download the reports, checkout to this branch and download the reports again and compare them * chore: Update translations (#12625) * chore: Migrate mailers from the worker to jobs (#12331) Previously, email replies were handled inside workers. There was no execution logs. This meant if emails silently failed (as reported by a customer), we had no way to trace where the issue happened, the only assumption was “no error = mail sent.” By moving email handling into jobs, we now have proper execution logs for each attempt. This makes it easier to debug delivery issues and would have better visibility when investigating customer reports. Fixes https://linear.app/chatwoot/issue/CW-5538/emails-are-not-sentdelivered-to-the-contact --------- Co-authored-by: Sojan Jose <sojan@pepalo.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> * chore(deps-dev): bump vite from 5.4.20 to 5.4.21 (#12700) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.20 to 5.4.21. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/vitejs/vite/releases">vite's releases</a>.</em></p> <blockquote> <h2>v5.4.21</h2> <p>Please refer to <a href="https://github.com/vitejs/vite/blob/v5.4.21/packages/vite/CHANGELOG.md">CHANGELOG.md</a> for details.</p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/vitejs/vite/blob/v5.4.21/packages/vite/CHANGELOG.md">vite's changelog</a>.</em></p> <blockquote> <h2><!-- raw HTML omitted -->5.4.21 (2025-10-20)<!-- raw HTML omitted --></h2> <ul> <li>fix(dev): trim trailing slash before <code>server.fs.deny</code> check (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20968">#20968</a>) (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20970">#20970</a>) (<a href=" |
||
|
|
58ca82c720
|
feat: Backend - Companies API endpoint with pagination and search (#12840)
## Description Adds API endpoint to list companies with pagination, search, and sorting. Fixes https://linear.app/chatwoot/issue/CW-5930/add-backend-routes-to-get-companies-result Parent issue: https://linear.app/chatwoot/issue/CW-5928/add-companies-tab-to-dashboard ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? Added comprehensive specs to `spec/enterprise/controllers/api/v1/accounts/companies_controller_spec.rb`: - Pagination (25 per page, multiple pages) - Search by name and domain (case-insensitive) - Counter cache for contacts_count - Account scoping - Authorization To reproduce: ```bash bundle exec rspec spec/enterprise/controllers/api/v1/accounts/companies_controller_spec.rb bundle exec rubocop enterprise/app/controllers/api/v1/accounts/companies_controller.rb ``` ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Sojan Jose <sojan@pepalo.com> |
||
|
|
c9823d9409
|
feat: Assignment service (v2) (#12320)
## Linear Link ## Description This PR introduces a new robust auto-assignment system for conversations in Chatwoot. The system replaces the existing round-robin assignment with a more sophisticated service-based architecture that supports multiple assignment strategies, rate limiting, and Enterprise features like capacity-based assignment and balanced distribution. ## Type of change - [ ] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - Unit test cases - Test conversations getting assigned on status change to open - Test the job directly via rails console ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a new service-based auto-assignment system with scheduled jobs, rate limiting, enterprise capacity/balanced selection, and wiring via inbox/handler; includes Redis helpers and comprehensive tests. > > - **Auto-assignment v2 (core services)**: > - Add `AutoAssignment::AssignmentService` with bulk assignment, configurable conversation priority, RR selection, and per-agent rate limiting via `AutoAssignment::RateLimiter`. > - Add `AutoAssignment::RoundRobinSelector` for agent selection. > - **Jobs & scheduling**: > - Add `AutoAssignment::AssignmentJob` (per-inbox bulk assign; env-based limit) and `AutoAssignment::PeriodicAssignmentJob` (batch over accounts/inboxes). > - Schedule periodic run in `config/schedule.yml` (`periodic_assignment_job`). > - **Model/concerns wiring**: > - Include `InboxAgentAvailability` in `Inbox`; add `Inbox#auto_assignment_v2_enabled?`. > - Update `AutoAssignmentHandler` to trigger v2 job when `auto_assignment_v2_enabled?`, else fallback to legacy. > - **Enterprise extensions**: > - Add `Enterprise::InboxAgentAvailability` (capacity-aware filtering) and `Enterprise::Concerns::Inbox` association `inbox_capacity_limits`. > - Extend service via `Enterprise::AutoAssignment::AssignmentService` (policy-driven config, capacity filtering, exclusion rules) and add selectors/services: `BalancedSelector`, `CapacityService`. > - **Infrastructure**: > - Enhance `Redis::Alfred` with `expire`, key scan/count, and extended ZSET helpers (`zadd`, `zcount`, `zcard`, `zrangebyscore`). > - **Tests**: > - Add specs for jobs, core service, rate limiter, RR selector, and enterprise features (capacity, balanced selection, exclusions). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 0ebe187c8aea73765b0122a44b18d6f465c2477f. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> |
||
|
|
1fbdd68222
|
feat: Add company auto-association for contacts (CW-5726 Part 2) (#12711)
## Description Implements real-time company auto-association for contacts based on email domains. This is **Part 2** of the company model production rollout (CW-5726). **Task:** - When a contact is created with a business email, automatically create and associate a company from the email domain - When a contact is updated with an email for the first time (email was previously nil), associate with a company - Preserve existing company associations when email changes to avoid user confusion - Skip free email providers and disposable domains **Dependencies:** ⚠️ Requires PR #12657 (Part 1: Backfill migration) to be merged first **Linear ticket:** [CW-5726](https://linear.app/chatwoot/issue/CW-5726/company-model-setting-it-up-on-production) ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - Service specs: Tests business email detection, company creation, association logic, edge cases (existing companies, free emails, nil emails) - Integration specs: Tests full callback flow for contact create/update scenarios - All tests passing: 10 examples, 0 failures - RuboCop: 0 offenses ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules (PR #12657 pending) --------- Co-authored-by: Sojan Jose <sojan@pepalo.com> |
||
|
|
ef54f07d5b
|
feat: Add company backfill migration for existing contacts (Part 1) (#12657)
## Description Implements company backfill migration infrastructure for existing contacts. This is **Part 1 of 2** for the company model production rollout as described in [CW-5726](https://linear.app/chatwoot/issue/CW-5726/company-model-setting-it-up-on-production). Creates jobs and services to associate existing contacts with companies based on their email domains, filtering out free email providers (gmail, yahoo, etc.) and disposable addresses. **What's included:** - Business email detector service with ValidEmail2 (uses `disposable_domain?` to avoid DNS lookups) - Per-account batch job to process contacts for one account - Orchestrator job to iterate all accounts - Rake task: `bundle exec rake companies:backfill` ~~*NOTE*: I'm using a hard-coded approach to determine if something is a "business" email by filtering out emails that are usually personal. I've also added domains that are common to some of our customers' regions. This should be simpler. I looked into `Valid_Email2` and I couldn't find anything to dictate whether an email is a personal email or a business one. I don't think the approach used in the frontend is valid here.~~ UPDATE: Using `email_provider_info` gem instead. **Pending - Part 2 (separate PR):** Real-time company creation for new contacts ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? ```bash # Run all new tests bundle exec rspec spec/enterprise/services/companies/business_email_detector_service_spec.rb \\ spec/enterprise/jobs/migration/company_account_batch_job_spec.rb \\ spec/enterprise/jobs/migration/company_backfill_job_spec.rb # Run RuboCop bundle exec rubocop enterprise/app/services/companies/business_email_detector_service.rb \\ enterprise/app/jobs/migration/company_account_batch_job.rb \\ enterprise/app/jobs/migration/company_backfill_job.rb \\ lib/tasks/companies.rake ``` **Performance optimization:** - Uses `disposable_domain?` instead of `disposable?` to avoid DNS MX lookups (discovered via tcpdump analysis - `disposable?` was making network calls for every email, causing 100x slowdown) ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sojan Jose <sojan@pepalo.com> |
||
|
|
358a3924b8
|
chore: Add dependant destroy_async for sla events (#12774)
Added the destroy_async to prevent timeout during SLA policy deletion by processing SLA events asynchronously. |
||
|
|
38af08534c
|
fix: Captain response builder not getting triggered (#12729)
## Summary - Fix captain response builder not getting triggered for cases where responses are created as completed. ## Testing Instructions - Test articles with firecrawl - Test articles without firecrawl - Test PDF documents --------- Co-authored-by: Pranav <pranav@chatwoot.com> |
||
|
|
74fc9c0321
|
fix: parameterize agent name (#12709) | ||
|
|
8d4a6b856a | Merge branch main into chore/merge-upstream-4.7.0 | ||
|
|
170ea7691f
|
feat: Add company model and API with tests (#12548)
# Pull Request Template ## Description * add Company model with validations for name, domain, description and avatar * Add database migration fo * Implement endpoints for company CRUD operations * Add optional company relationship for contacts * Add test for models, controllers, factories and policies * Add authorization policies restricting delete to admins * support JSON API responses Please include a summary of the change and issue(s) fixed. Also, mention relevant motivation, context, and any dependencies that this change requires. Fixes #(cw-5650) ## Type of change Please delete options that are not relevant. - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Tests are implemented using `RSpec` ``` $ bundle exec rails db:migrate $ bundle exec rspec spec/models/company_spec.rb spec/controllers/api/v1/accounts/companies_controller_spec.rb ``` ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
3a71829b46
|
feat: Improve captain conversation handling (#12599)
Co-authored-by: Pranav <pranavrajs@gmail.com> |
||
|
|
9fb0dfa4a7
|
feat: Add UI for custom tools (#12585)
### Tools list <img width="2316" height="666" alt="CleanShot 2025-10-03 at 20 42 41@2x" src="https://github.com/user-attachments/assets/ccbffd16-804d-4eb8-9c64-2d1cfd407e4e" /> ### Tools form <img width="2294" height="2202" alt="CleanShot 2025-10-03 at 20 43 05@2x" src="https://github.com/user-attachments/assets/9f49aa09-75a1-4585-a09d-837ca64139b8" /> ## Response <img width="800" height="2144" alt="CleanShot 2025-10-03 at 20 45 56@2x" src="https://github.com/user-attachments/assets/b0c3c899-6050-4c51-baed-c8fbec5aae61" /> --------- Co-authored-by: Pranav <pranavrajs@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com> |
||
|
|
8bbb8ba5a4
|
feat(ee): Captain custom http tools (#12584)
To test this out, use the following PR: https://github.com/chatwoot/chatwoot/pull/12585 --------- Co-authored-by: Pranav <pranavrajs@gmail.com> |
||
|
|
18c672c204 | Merge branch 'main' into chore/merge-upstream-4.6.0 | ||
|
|
7a453f50f4
|
feat: update users on SAML setup and destroy [CW-2958][CW-5612] (#12346) |