d154edf8ea
212 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d3ce2a4cf9 | Merge branch 'main' into chore/merge-upstream-4.12.0 | ||
|
|
efe49f7da4
|
fix: captain liquid render file system (#13647) | ||
|
|
b98c614669
|
feat: add campaign context to Captain v2 prompts (#13644)
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com> |
||
|
|
55f6257313
|
chore(hub): clean up legacy Captain hub flow (#13640)
## Summary This PR cleans up legacy Hub/Captain integration paths and simplifies hub URL behavior coverage in tests. ## Changes - remove legacy Captain account endpoint flow from `ChatwootHub` - remove obsolete spec coverage tied to that retired flow - keep hub URL handling centralized in `base_url` with enterprise overlay precedence - simplify hub URL specs to assert explicit static URL expectations where applicable ## Reproduce Run the focused hub specs from this branch: - `bundle exec rspec spec/lib/chatwoot_hub_spec.rb spec/enterprise/lib/chatwoot_hub_spec.rb` ## Testing Validated locally with: - `bundle exec rspec spec/lib/chatwoot_hub_spec.rb spec/enterprise/lib/chatwoot_hub_spec.rb` - `bundle exec rubocop lib/chatwoot_hub.rb spec/lib/chatwoot_hub_spec.rb enterprise/lib/enterprise/chatwoot_hub.rb spec/enterprise/lib/chatwoot_hub_spec.rb` |
||
|
|
7cec4ebaae
|
feat: support multimodal user messages in captain v2 (#13581)
Extract and pass image attachments from the latest user message to the runner, excluding the last user message from the context for processing. Fixes #13588 # Pull Request Template ## Description Adds image support to captain v2 ## Type of change Please delete options that are not relevant. - [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 local testing <img width="754" height="1008" alt="image" src="https://github.com/user-attachments/assets/914cbc2c-9d30-42d0-87d4-9e5430845c87" /> langfuse also shows media correctly with the instrumentation code: <img width="1800" height="1260" alt="image" src="https://github.com/user-attachments/assets/ce0f5fa6-b1a5-42ec-a213-9a82b1751037" /> ## 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> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
9dd13b9a2b
|
fix: topup checkout flaky test (#13616) | ||
|
|
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> |
||
|
|
db7e02b93b
|
feat: captain channel type langfuse metadata (#13574)
# Pull Request Template ## Description Adds channel type to Captain assistant traces in Langfuse ## 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="906" height="672" alt="image" src="https://github.com/user-attachments/assets/224cee95-56aa-4672-8f74-0c0052251db9" /> <img width="908" height="611" alt="image" src="https://github.com/user-attachments/assets/ddd8ef0d-47c1-450c-a09f-27e82a34d04d" /> ## 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> |
||
|
|
2ab117e8eb
|
feat(cloud-billing): cancel subscriptions at period end on deletion mark (#13580)
## How to reproduce In Chatwoot Cloud, mark an account for deletion from account settings while the account has an active Stripe subscription. Before this change, deletion marking did not explicitly mark subscriptions to stop renewing at period end. ## What changed This PR adds `Enterprise::Billing::CancelCloudSubscriptionsService` and calls it from the delete action path in `Enterprise::Api::V1::AccountsController`. The service lists only active Stripe subscriptions for the customer and sets `cancel_at_period_end: true` when needed. The account deletion schedule remains unchanged (existing static 7-day behavior), and Stripe deleted-event fallback behavior remains unchanged. ## How this was tested Added and updated specs: - `spec/enterprise/services/enterprise/billing/cancel_cloud_subscriptions_service_spec.rb` - `spec/enterprise/controllers/enterprise/api/v1/accounts_controller_spec.rb` Executed: - `bundle exec rspec spec/enterprise/services/enterprise/billing/cancel_cloud_subscriptions_service_spec.rb` - `bundle exec rspec spec/enterprise/controllers/enterprise/api/v1/accounts_controller_spec.rb:363` |
||
|
|
9a4c5058f3 | Merge branch 'main' into chore/merge-upstream-4.11.0 | ||
|
|
138840a23f
|
fix: typo in metadata key in captain v2 (#13558)
# Pull Request Template ## Description ## Type of change typo fix ## 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 |
||
|
|
3874383698
|
feat: insrument captain v2 (#13439)
# Pull Request Template ## Description Instruments captain v2 ## 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. Local testing: <img width="864" height="510" alt="image" src="https://github.com/user-attachments/assets/855ebce5-e8b8-4d22-b0bb-0d413769a6ab" /> ## 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> |
||
|
|
61eaa098ae
|
fix(messages): reduce audio transcription 400 retry noise (#13487)
## Summary This PR reduces duplicate failure noise for audio transcription jobs that fail with permanent HTTP 400 responses, and fixes a file-format edge case causing intermittent 400s. Sentry issue: [CHATWOOT-99E / 6660541334](https://chatwoot-p3.sentry.io/issues/6660541334/) ## Confirmed root cause For some attachments, the stored filename had no extension (example: `speech`, content type `audio/mpeg`). When the temporary transcription upload file was created without an extension, OpenAI returned: `Unrecognized file format` (HTTP 400). ## Scope of changes 1. `Messages::AudioTranscriptionJob` - Keeps `discard_on Faraday::BadRequestError` to avoid retry storms on permanent request errors. - Adds explicit Rails warning logs for discarded jobs with attachment/job/status context. 2. `Messages::AudioTranscriptionService` - Keeps guaranteed temp file cleanup via `ensure`. - Ensures temp upload files include an extension when the original filename has none, derived from blob `content_type`. - This addresses intermittent failures like extensionless `audio/mpeg` files. ## Reproduction Enable audio transcription for an account and process an audio attachment whose stored filename has no extension (for example `speech`) but valid audio content type (`audio/mpeg`). Before this fix, OpenAI transcription could return HTTP 400 `Unrecognized file format` for that attachment while similar attachments with extensions succeeded. ## Testing Ran: `bundle exec rubocop enterprise/app/jobs/messages/audio_transcription_job.rb enterprise/app/services/messages/audio_transcription_service.rb` Result: both modified files pass lint with no offenses. |
||
|
|
2c2f0547f7
|
fix: Captain not responding to campaign conversations (#13489)
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com> |
||
|
|
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> |
||
|
|
6e397c7571
|
fix: default model for captain assistant (#13496) | ||
|
|
e9e6de5690
|
fix: Increase the parallelism config to fix flaky tests, revert bad commits (#13410)
The specs break only in Circle CI, we have to figure out the root cause for the same. At the moment, I have increased the parallelism to fix this. |
||
|
|
5ec77aca64
|
feat: Add first response time distribution report endpoint (#13400)
The index is already added in production.
Adds a new reporting API that returns conversation counts grouped by
channel type and first response time buckets (0-1h, 1-4h, 4-8h, 8-24h,
24h+).
- GET /api/v2/accounts/:id/reports/first_response_time_distribution
- Uses SQL aggregation to handle large datasets efficiently
- Adds composite index on reporting_events for query performance
Tested on production workload.
Request: GET
`/api/v2/accounts/1/reports/first_response_time_distribution?since=<since>&until=<until>`
Response payload:
```
{
"Channel::WebWidget": {
"0-1h": 120,
"1-4h": 85,
"4-8h": 32,
"8-24h": 12,
"24h+": 3
},
"Channel::Email": {
"0-1h": 12,
"1-4h": 28,
"4-8h": 45,
"8-24h": 35,
"24h+": 10
},
"Channel::FacebookPage": {
"0-1h": 50,
"1-4h": 30,
"4-8h": 15,
"8-24h": 8,
"24h+": 2
}
}
```
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
|
||
|
|
77493c5d0f
|
fix: captain assistant image comprehension (#13390)
# Pull Request Template ## Description Fixes # (issue) When we migrated to RubyLLM, images weren't being sent properly in RubyLLM format to the model, so it did not understand images. ## Type of change Please delete options that are not relevant. - [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 + local testing Current behaviour on staging: <img width="772" height="1012" alt="image" src="https://github.com/user-attachments/assets/7b7d360f-dea4-48af-b20b-ee4c98a38a85" /> local testing with fix: <img width="792" height="1216" alt="image" src="https://github.com/user-attachments/assets/5ef82452-015e-4bda-a68f-884d00acb014" /> ## 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> |
||
|
|
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> |
||
|
|
6a482926b4
|
feat: new Captain Editor (#13235)
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com> Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: aakashb95 <aakashbakhle@gmail.com> |
||
|
|
b2ffad1998
|
fix: Validate status and priority params in search conversations tool (#13295)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> |
||
|
|
6ab1898992 | Merge branch 'main' into chore/merge-upstream-4.10 | ||
|
|
a8b302d4cd
|
feat(ee): Review Notes for CSAT Reports (#13289)
CSAT scores are helpful, but on their own they rarely tell the full story. A drop in rating can come from delayed timelines, unclear expectations, or simple misunderstandings, even when the issue itself was handled correctly. Review Notes for CSAT let admins/report manager roles add internal-only context next to each CSAT response. This makes it easier to interpret scores properly and focus on patterns and root causes, not just numbers. <img width="2170" height="1680" alt="image" src="https://github.com/user-attachments/assets/56df7fab-d0a7-4a94-95b9-e4c459ad33d5" /> ### Why this matters * Capture the real context behind individual CSAT ratings * Clarify whether a low score points to a genuine service issue or a process gap * Spot recurring themes across conversations and teams * Make CSAT reviews more useful for leadership reviews and retrospectives ### How Review Notes work **View CSAT responses** Open the CSAT report to see overall metrics, rating distribution, and individual responses. **Add a Review Note** For any CSAT entry, managers can add a Review Note directly below the customer’s feedback. **Document internal insights** Use Review Notes to capture things like: * Why a score was lower or higher than expected * Patterns you are seeing across similar cases * Observations around communication, timelines, or customer expectations Review Notes are visible only to administrators and people with report access only. We may expand visibility to agents in the future based on feedback. However, customers never see them. Each note clearly shows who added it and when, making it easy to review context and changes over time. |
||
|
|
b099d3a1eb
|
fix: PDF errors not loading in CI (#13236) | ||
|
|
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> |
||
|
|
dcc72ee63c
|
fix: consistent instrumentation with conversation.display_id (#13194) | ||
|
|
566de02385
|
feat: allow agent bot and captain responses to reset waiting since (#13181)
When AgentBot responds to customer messages, the `waiting_since`
timestamp is not reset, causing inflated reply time metrics when a human
agent eventually responds. This results in inaccurate reporting that
incorrectly includes periods when customers were satisfied with bot
responses.
### Timeline from Production Data
```
Dec 12, 16:20:14 - Customer sends message (ID: 368451924)
↓ waiting_since = Dec 12, 16:20:14
Dec 12, 16:20:17 - AgentBot replies (ID: 368451960)
↓ waiting_since STILL = Dec 12, 16:20:14 ❌
↓ (Bot response doesn't clear it)
14-day gap - Customer satisfied, no messages
↓ waiting_since STILL = Dec 12, 16:20:14 ❌
Dec 26, 22:25:45 - Customer sends new message (ID: 383522275)
↓ waiting_since STILL = Dec 12, 16:20:14 ❌
↓ (New message doesn't reset it)
Dec 26-27 - More AgentBot interactions
↓ waiting_since STILL = Dec 12, 16:20:14 ❌
Dec 27, 07:36:53 - Human agent finally replies (ID: 383799517)
↓ Reply time calculated: 1,268,404 seconds
↓ = 14.7 DAYS ❌
```
## Root Cause
The core issues is in `app/models/message.rb`, where **AgentBot messages
does not clear `waiting_since`** - The `human_response?` method only
returns true for `User` senders, so bot replies never trigger the
clearing logic. This means once `waiting_since` is set, it stays set
even when customers send new messages after receiving bot responses.
The solution is to simply reset `waiting_since` **after a bot has
responded**. This ensures reply time metrics reflect actual human agent
response times, not bot-handled periods.
### What triggers the rest
This is an intentional "gotcha", that only `AgentBot` and
`Captain::Assistant` messages trigger the waiting time reset. Automation
and campaign messages maintain current behavior (no reset). This is
because interactive bot assistants provide conversational help that
might satisfy customers. Automation and campaigns are one-way
communications and shouldn't affect waiting time calculations.
## Related Work
Extends PR #11787 which fixed `waiting_since` clearing on conversation
resolution. This PR addresses the bot interaction scenario which was not
covered by that fix.
Scripts to clean data:
https://gist.github.com/scmmishra/bd133208e219d0ab52fbfdf03036c48a
|
||
|
|
6d9a344186 | chore: fix linting | ||
|
|
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 |
||
|
|
3fce56c98f
|
fix: captain template message conflict (#13048)
Co-authored-by: aakashb95 <aakash@chatwoot.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com> |
||
|
|
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.
|
||
|
|
3051da1e44
|
fix: add renewal identification for credit flow (#12999) | ||
|
|
eb759255d8
|
perf: update the logic to purchase credits (#12998)
## Description - Replaces Stripe Checkout session flow with direct card charging for AI credit top-ups - Adds a two-step confirmation modal (select package → confirm purchase) for better UX - Creates Stripe invoice directly and charges the customer's default payment method immediately ## Type of change - [ ] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - Using the specs - UI manual test cases <img width="945" height="580" alt="image" src="https://github.com/user-attachments/assets/52bdad46-cd0e-4927-b13f-54c6b6353bcc" /> <img width="945" height="580" alt="image" src="https://github.com/user-attachments/assets/231bc7e9-41ac-440d-a93d-cba45a4d3e3e" /> ## 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: Shivam Mishra <scm.mymail@gmail.com> |
||
|
|
cc86b8c7f1
|
fix: stream attachment handling in workers (#12870)
We’ve been watching Sidekiq workers climb from ~600 MB at boot to 1.4–1.5 GB after an hour whenever attachment-heavy jobs run. This PR is an experiment to curb that growth by streaming attachments instead of loading the whole blob into Ruby: reply-mailer inline attachments, Telegram uploads, and audio transcriptions now read/write in chunks. If this keeps RSS stable in production we’ll keep it; otherwise we’ll roll it back and keep digging |
||
|
|
eed2eaceb0
|
feat: Migrate ruby llm captain (#12981)
Co-authored-by: aakashb95 <aakash@chatwoot.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> |
||
|
|
87fe1e9ad7
|
feat: migrate editor to ruby-llm (#12961)
Co-authored-by: aakashb95 <aakash@chatwoot.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> |
||
|
|
b269cca0bf
|
feat: Add AI credit topup flow for Stripe (#12988)
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com> |
||
|
|
1ef945de7b
|
feat: Instrument captain (#12949)
Co-authored-by: aakashb95 <aakash@chatwoot.com> |
||
|
|
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=" |
||
|
|
08b9134486
|
feat: speed up circleci and github actions (#12849)
# 🚀 Speed up CI/CD test execution with parallelization ## TL;DR - **Problem**: CI tests took 36-42 minutes per commit, blocking developer workflow - **Solution**: Implemented 16-way parallelization + optimized slow tests + fixed Docker builds - **Impact**: **1,358 hours/month saved** (7.7 FTE) across GitHub Actions + CircleCI - GitHub Actions tests: 36m → 7m (82% faster) - Backend tests: 28m → 4m (87% faster with 16-way parallelization) - CircleCI tests: 42m → 7m (83% faster) - Docker builds: 34m → 5m (85% faster) - **Result**: 5-6x faster feedback loops, 100% success rate on recent runs --- ## Problem CI test runs were taking **36-42 minutes per commit push** (GitHub Actions: 36m avg, CircleCI: 42m P95), creating a significant bottleneck in the development workflow. ## Solution This PR comprehensively restructures both CI test pipelines to leverage 16-way parallelization and optimize test execution, reducing test runtime from **36-42 minutes to ~7 minutes** - an **82% improvement**. --- ## 📊 Real Performance Data (Both CI Systems) ### GitHub Actions #### Before (develop branch - 5 recent runs) ``` Individual runs: 35m 29s | 36m 1s | 40m 0s | 36m 4s | 34m 18s Average: 36m 22s ``` #### After (feat/speed_up_ci branch - 9 successful runs) ``` Individual runs: 6m 39s | 7m 2s | 6m 53s | 6m 26s | 6m 52s | 6m 42s | 6m 45s | 6m 40s | 6m 37s Average: 6m 44s Range: 6m 26s - 7m 2s ``` **Improvement**: ⚡ **81.5% faster** (29m 38s saved per run) #### Backend Tests Specific Impact With 16-way parallelization, backend tests show dramatic improvement: - **Before**: 27m 52s (sequential execution) - **After**: 3m 44s (longest of 16 parallel runners) - Average across runners: 2m 30s - Range: 1m 52s - 3m 44s - **Improvement**: ⚡ **86.6% faster** (24m 8s saved) --- ### CircleCI #### Before (develop branch - CircleCI Insights) ``` Duration (P95): 41m 44s Runs: 70 (last 30 days) Success Rate: 84% ``` #### After (feat/speed_up_ci branch - Last 2 pipeline runs) ``` Run 1 (1h ago): 7m 7s ├─ lint: 4m 12s ├─ frontend-tests: 5m 36s ├─ backend-tests: 6m 23s ├─ coverage: 20s └─ build: 1s Run 2 (2h ago): 7m 21s ├─ lint: 3m 47s ├─ frontend-tests: 5m 4s ├─ backend-tests: 6m 33s ├─ coverage: 19s └─ build: 1s Average: 7m 14s Success Rate: 100% ✅ ``` **Improvement**: ⚡ **82.7% faster** (34m 30s saved per run) --- ## 🐳 Related Work: Docker Build Optimization As part of the broader CI/CD optimization effort, Docker build performance was improved separately in **PR #12859**. ### Docker Build Fix (Merged Separately) **Problem**: Multi-architecture Docker builds (amd64/arm64) were taking ~34 minutes due to cache thrashing **Solution**: Added separate cache scopes per platform in `.github/workflows/test_docker_build.yml`: ```yaml cache-from: type=gha,scope=${{ matrix.platform }} cache-to: type=gha,mode=max,scope=${{ matrix.platform }} ``` **Results** (measured from November 2025 data): - **Before**: 34.2 minutes/run average (15,547 minutes across 454 runs) - **After**: 5 minutes/run - **Improvement**: 85% faster, 29.2 minutes saved per run - **Frequency**: 25.2 runs/day - **Monthly savings**: **369 hours** (46 developer-days) This prevents different architectures from invalidating each other's caches and contributes 27% of total CI/CD time savings. --- ## 🎯 Key Findings ### Both CI Systems Now Perform Similarly - **CircleCI**: 7m 14s average - **GitHub Actions**: 6m 44s average - **Difference**: Only 30 seconds apart (remarkably consistent!) ### Combined Performance - **Average improvement across both systems**: **82.1% faster** - **Time saved per commit**: ~32 minutes - **Developer feedback loop**: 36-42 minutes → ~7 minutes ### Success Rate Improvement - **CircleCI**: 84% → 100% (on feat/speed_up_ci branch) - **GitHub Actions**: 100% (all 9 recent runs successful) - Fixed all test isolation issues that caused intermittent failures ### Impact at Scale (Based on Real November 2025 Data) - **CI runs per day**: **30.8 average** for tests, **25.2** for Docker builds - Measured from GitHub Actions Usage Metrics (18 days) - Weekdays: 38-54 runs/day - Peak: up to 68 runs in a single day - **This PR (test suite only)**: - **Daily time saved**: **15.3 hours** (GitHub Actions + CircleCI) - **Monthly time saved**: **458 hours** (57 developer-days) on GitHub Actions - Additional **531 hours** (66 developer-days) on CircleCI - **Combined with Docker optimization** (PR #12859): **1,358 hours/month** (see Summary) - **Developer experience**: 5-6x faster iteration cycles --- ## Code Changes ### 1. **Backend Test Parallelization (16x)** Both CI systems now use 16-way parallelization with identical round-robin test distribution: ```bash # Distribute tests evenly across 16 runners SPEC_FILES=($(find spec -name '*_spec.rb' | sort)) for i in "${!SPEC_FILES[@]}"; do if [ $(( i % 16 )) -eq $RUNNER_INDEX ]; then TESTS="$TESTS ${SPEC_FILES[$i]}" fi done ``` **Why round-robin over timing-based?** - CircleCI's timing-based splitting grouped similar tests together - This caused race conditions with OAuth callback tests (Linear, Shopify, Notion) - Round-robin ensures even distribution and better test isolation - Both CI systems now behave identically ### 2. **Frontend Test Optimization** Enabled Vitest thread parallelization in `vite.config.ts`: ```typescript pool: 'threads', poolOptions: { threads: { singleThread: false, }, }, ``` ### 3. **CI Architecture Restructuring** Split monolithic CI jobs into parallel stages: - **Lint** (backend + frontend) - runs independently for fast feedback - **Frontend tests** - runs in parallel with backend - **Backend tests** - 16-way parallelized across runners - **Coverage** - aggregates results from test jobs - **Build** (CircleCI only) - final job for GitHub status check compatibility ### 4. **Critical Test Optimization** **report_builder_spec.rb**: Changed `before` to `before_all` - Reduced execution time from **19 minutes to 1.2 minutes** (16x speedup) - Setup now runs once instead of 21 times - Single biggest performance improvement after parallelization --- ## Test Stability Fixes (10 spec files) Parallelization exposed latent test isolation issues that were fixed: ### Object Identity Comparisons (6 files) Tests were comparing Ruby object instances instead of IDs: - `spec/models/integrations/hook_spec.rb` - Use `.pluck(:id)` for comparisons - `spec/enterprise/models/captain/scenario_spec.rb` - Compare IDs instead of objects - `spec/models/notification_spec.rb` - Compare IDs for sort order validation - `spec/models/account_spec.rb` - Compare IDs in scope queries - `spec/services/widget/token_service_spec.rb` - Compare class names instead of class objects - `spec/models/concerns/avatarable_shared.rb` - Use `respond_to` checks for ActiveStorage ### Database Query Caching - `spec/jobs/delete_object_job_spec.rb` - Added `.reload` to force fresh database queries ### Test Expectations Timing - `spec/jobs/mutex_application_job_spec.rb` - Removed flaky unlock expectation after error block - Related to original PR #8770 - Expectation after error block never executes in parallel environments ### Timezone Handling - `spec/mailers/account_notification_mailer_spec.rb` - Fixed date parsing at timezone boundaries - Changed test time from 23:59:59Z to 12:00:00Z ### Test Setup - `spec/builders/v2/report_builder_spec.rb` - Optimized with `before_all` --- ## CircleCI GitHub Integration Fix ### Problem GitHub PR checks were stuck on "Waiting for status" even when all CircleCI jobs passed. GitHub was expecting a job named `build` but the workflow only had a workflow named "build". ### Solution Added an explicit `build` job that runs after all other jobs: ```yaml build: steps: - run: name: Legacy build aggregator command: echo "All main jobs passed" requires: - lint - coverage ``` This ensures GitHub's required status checks work correctly. --- ## ✅ Testing & Validation - ✅ **GitHub Actions**: 9 successful runs, consistent 6m 26s - 7m 2s runtime - ✅ **CircleCI**: 2 successful runs, consistent 7m 7s - 7m 21s runtime - ✅ Both CI systems produce identical, consistent results - ✅ GitHub PR status checks complete correctly - ✅ Success rate improved from 84% to 100% (recent runs) - ✅ No test regressions introduced - ✅ All flaky tests fixed (callback controllers, mutex jobs, etc.) --- ## 🎉 Summary This PR delivers an **82% improvement** in test execution time across both CI systems: - **GitHub Actions tests**: 36m → 7m (81.5% faster) - Backend tests specifically: 28m → 4m (86.6% faster) - **CircleCI tests**: 42m → 7m (82.7% faster) - **Developer feedback loop**: 5-6x faster - **Test stability**: 84% → 100% success rate ### 📊 Total CI/CD Impact (All Optimizations) Based on real November 2025 data, combining this PR with Docker build optimization (PR #12859): **Monthly Time Savings**: **1,358 hours/month** = **170 developer-days/month** = **7.7 FTE** | System | Runs/Day | Before | After | Savings | Monthly Impact | |--------|----------|---------|--------|---------|----------------| | **GitHub Actions Tests** | 30.8 | 36.5m | 6.7m | 29.8m/run | 458 hrs (34%) | | **GitHub Actions Docker** | 25.2 | 34.2m | 5.0m | 29.2m/run | 369 hrs (27%) | | **CircleCI Tests** | 30.8 | 41.7m | 7.2m | 34.5m/run | 531 hrs (39%) | *Data source: GitHub Actions Usage Metrics (November 2025, 18 days), CircleCI Insights (30 days)* The combined optimizations save the equivalent of **nearly 8 full-time developers** worth of CI waiting time every month, significantly improving developer velocity and reducing CI costs. All test isolation issues exposed by parallelization have been fixed, ensuring reliable and consistent results across both CI platforms. woot woot !!! --------- |
||
|
|
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> |
||
|
|
4f09c2203c
|
feat: allow querying reporting events via the API (#12832) | ||
|
|
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> |