Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
b2ffad1
fix: Validate status and priority params in search conversations tool…
pranavrajs Jan 19, 2026
7e4d93f
fix: Setup webhooks for manual WhatsApp Cloud channel creation (#13278)
muhsin-k Jan 19, 2026
0346e9a
fix: captain inbox modal shows wrong assistant data (#13302)
scmmishra Jan 19, 2026
e13e3c8
feat: add report download task (#13250)
scmmishra Jan 19, 2026
457430e
fix: Remove `phone_number_id` param from WhatsApp media retrieval for…
muhsin-k Jan 20, 2026
ecd4892
Bump version to 4.10.1
sojan-official Jan 20, 2026
c77c9c9
Merge branch 'release/4.10.1' into develop
sojan-official Jan 20, 2026
6a48292
feat: new Captain Editor (#13235)
scmmishra Jan 21, 2026
f84e95e
fix: use safe DOM manipulation for article heading permalinks (#13239)
stonecharioteer Jan 21, 2026
cc5ec83
feat: check if label suggestion is enabled in hooks (#13331)
scmmishra Jan 21, 2026
70d09fc
fix: make llm aware about signatures in editor replies (#13332)
aakashb95 Jan 21, 2026
9e97cc0
fix(whatsapp): Preserve ordered list numbering in messages (#13339)
pranavrajs Jan 22, 2026
1f5fdd7
fix: Add Portuguese (Brazil) to CSAT template language options (#13343)
muhsin-k Jan 22, 2026
964d2f8
perf: use account.contacts directly in search to reduce DB load (#12956)
vishnu-narayanan Jan 22, 2026
75f75ce
fix: sanitize integer fields to prevent Elasticsearch mapping errors …
tds-1 Jan 22, 2026
8eb6fd1
feat: track copilot events (#13342)
scmmishra Jan 22, 2026
747d451
chore: Improve signup flow, reduce the number of inputs (#13350)
pranavrajs Jan 23, 2026
ad2329c
perf(conversations): throttle agent_last_seen_at updates to reduce DB…
pranavrajs Jan 24, 2026
885b041
fix: Update help center sitemap XML structure (#13357)
TheDanniCraft Jan 27, 2026
04b2901
feat: Conversation workflows(EE) (#13040)
muhsin-k Jan 27, 2026
7cddba2
feat: Add infinite scroll to contacts search page (#13376)
pranavrajs Jan 28, 2026
40c622e
fix: Tiktok nil conversation handling in ReadStatusService (#13152)
mazenkhalil Jan 28, 2026
68f0da7
fix: Attachments download authentication issue in Tiktok (#13151)
mazenkhalil Jan 28, 2026
b870a48
perf: limit the number of notifications per user to 300 (#13234)
tds-1 Jan 28, 2026
aaeea6c
feat: Display story replies with attachment and context label (#13356)
muhsin-k Jan 28, 2026
d166ae7
feat: add cron job to remove orphan conversations (#13335)
tds-1 Jan 28, 2026
3b612e2
chore: Add unsupported message for Tiktok (#13380)
muhsin-k Jan 28, 2026
6cd1b37
feat: Tiktok API version configurable via Super Admin (#13381)
muhsin-k Jan 28, 2026
0ca98bc
feat: add lightweight /health endpoint (#13386)
vishnu-narayanan Jan 28, 2026
2a69b37
chore: Update theme colors and add new Inter variable fonts (#13347)
iamsivin Jan 28, 2026
0d9c0b2
fix: Force account_id in the query (#13388)
pranavrajs Jan 28, 2026
77493c5
fix: captain assistant image comprehension (#13390)
aakashb95 Jan 29, 2026
acd0f14
feat: Add TikTok social profile display support (#13384)
muhsin-k Jan 29, 2026
9238b4c
feat: Add the ability to configure `WIDGET_TOKEN_EXPIRY` (#13385)
muhsin-k Jan 29, 2026
a32565d
fix: velma connection limit (#13395)
tds-1 Jan 29, 2026
6f45af6
feat: Add inbox-label matrix report endpoint (#13394)
muhsin-k Jan 29, 2026
81307d5
feat: search documentation tool for reply suggestions (#13340)
aakashb95 Jan 30, 2026
85324c8
fix: Formatting issue with reply preview content (#13399)
iamsivin Jan 30, 2026
5ec77ac
feat: Add first response time distribution report endpoint (#13400)
pranavrajs Jan 30, 2026
d8c5dda
chore: Update report documentation (#13408)
pranavrajs Jan 30, 2026
329b749
Add API documentation for inbox, agent, and team summary report (#13409)
pranavrajs Jan 30, 2026
e9e6de5
fix: Increase the parallelism config to fix flaky tests, revert bad c…
pranavrajs Jan 30, 2026
133fb1b
feat: add mark pending action to automation (#13378)
scmmishra Feb 2, 2026
b686d14
feat: Handle external echo messages from native apps (#13371)
muhsin-k Feb 2, 2026
c77d935
fix: Subscribe app to WABA before overriding webhook callback URL (#1…
muhsin-k Feb 2, 2026
c884cde
feat: add per-account daily rate limit for outbound emails (#13411)
vishnu-narayanan Feb 2, 2026
ef6ba8a
chore: Upgrade Rails to 7.2.2 and update Gemfile dependencies (#11037)
sojan-official Feb 3, 2026
9eb3ee4
Revert "chore: Upgrade Rails to 7.2.2 and update Gemfile dependencies…
sojan-official Feb 4, 2026
7ade906
feat: display total FAQ count in Related FAQs dialog (#13433)
tds-1 Feb 4, 2026
8eaea7c
feat: Add standalone outgoing messages count API endpoint (#13419)
muhsin-k Feb 4, 2026
053b777
fix: Render all account limit fields (#13435)
sojan-official Feb 4, 2026
04e747c
chore: temporarily disable `ProcessStaleContactsJob` (#13462)
scmmishra Feb 6, 2026
0d3b59f
feat: Refactor reports filters (#13443)
iamsivin Feb 6, 2026
0e30e3c
fix: add loading and silent retry to summary reports (#13455)
scmmishra Feb 6, 2026
6a7cbcf
fix: Fixes reply-to in WhatsApp Cloud API (#13467)
pranavrajs Feb 6, 2026
0a910c3
fix: Add email rate limiting to automation rule actions (#13474)
vishnu-narayanan Feb 7, 2026
f83415f
fix(account-deletion): normalize deleted email suffix and handle coll…
sojan-official Feb 8, 2026
656ae41
fix(imap): handle IMAP parser/read errors without exception tracking …
sojan-official Feb 8, 2026
04c456e
fix: handle 404 errors gracefully in avatar download job (#13491)
tds-1 Feb 9, 2026
6711264
fix: escape special characters in Linear GraphQL queries (#13490)
scmmishra Feb 9, 2026
bd732f1
fix: search faqs in account language (#13428)
aakashb95 Feb 9, 2026
6632610
chore(deps): bump faraday from 2.13.1 to 2.14.1 (#13503)
dependabot[bot] Feb 10, 2026
4622560
chore(dev): document codex worktree local setup (#13494)
sojan-official Feb 10, 2026
6e397c7
fix: default model for captain assistant (#13496)
aakashb95 Feb 10, 2026
b252656
fix: Prevent race condition in conversation dataFetched flag (#13492)
iamsivin Feb 10, 2026
e65ea24
fix: Wrong assignee displayed after switching conversations (#13501)
iamsivin Feb 10, 2026
0ad47d8
fix: Use Faraday for Telegram document uploads to fix large file fail…
muhsin-k Feb 10, 2026
8f95faf
feat: Add a setting to keep conversations pending on bot failures (#1…
pranavrajs Feb 11, 2026
7b512bd
fix: V2 Assignment service enhancements (#13036)
tds-1 Feb 11, 2026
00ed074
fix: disable email transcript for free plans (#13509)
vishnu-narayanan Feb 11, 2026
b2cb371
fix: Replace default Rails error pages with custom designs (#13514)
sojan-official Feb 11, 2026
d272a64
fix(mailbox): handle malformed sender address headers (#13486)
sojan-official Feb 11, 2026
c7193c7
fix(slack): handle archived channel errors in SendOnSlackJob (#13520)
sojan-official Feb 12, 2026
2c2f054
fix: Captain not responding to campaign conversations (#13489)
scmmishra Feb 12, 2026
4d362da
fix: Prevent user enumeration on password reset endpoint (#13528)
joao-baza Feb 13, 2026
6b7180d
fix(twilio): prevent dead jobs on missing channel lookup (#13522)
sojan-official Feb 13, 2026
fd5ac2a
fix: apply installation branding replacement in tooltip copy (#13538)
sojan-official Feb 14, 2026
f4538ae
fix: Enforce team boundaries to prevent cross-team assignments (#13353)
tds-1 Feb 16, 2026
9cd7c4e
fix: Enhance notification emails with message details and handle fail…
tds-1 Feb 16, 2026
61eaa09
fix(messages): reduce audio transcription 400 retry noise (#13487)
sojan-official Feb 17, 2026
101eca3
feat: add captain editor events (#13524)
aakashb95 Feb 17, 2026
3874383
feat: insrument captain v2 (#13439)
aakashb95 Feb 17, 2026
aa7e3c2
feat: langfuse logging improvements (#13534)
aakashb95 Feb 17, 2026
cfe3061
feat: Allow removing labels via conversation context menu (#13525)
iamsivin Feb 17, 2026
fb2f5e1
fix: Persist compose form state on accidental outside click (#13529)
iamsivin Feb 17, 2026
39243b9
fix: duplicate `message_created` webhooks for WhatsApp messages (#13523)
scmmishra Feb 17, 2026
c5f6844
fix: Disable reply editor outside WhatsApp reply window (#13454)
iamsivin Feb 17, 2026
138840a
fix: typo in metadata key in captain v2 (#13558)
aakashb95 Feb 17, 2026
229f56d
chore: Remove `vue-multiselect` and migrate to next components (#13506)
iamsivin Feb 17, 2026
e75e8a7
feat(shopify): Add mandatory compliance webhooks with HMAC verificati…
muhsin-k Feb 17, 2026
dae4f3e
fix: move llm call of captain outside transaction (#13559)
aakashb95 Feb 17, 2026
e815264
Bump version to 4.11.0
sojan-official Feb 17, 2026
a238034
Merge branch 'release/4.11.0' into develop
sojan-official Feb 17, 2026
594333a
chore(deps): bump rack from 3.2.3 to 3.2.5 (#13569)
dependabot[bot] Feb 18, 2026
9a4c505
Merge branch 'main' into chore/merge-upstream-4.11.0
gabrieljablonski Feb 18, 2026
248d6c2
feat: add external_created_at to message creation and update specs fo…
gabrieljablonski Feb 18, 2026
70f7f5c
chore: rubocop
gabrieljablonski Feb 18, 2026
85ec13a
chore: update DropdownBody styles for better overflow handling
gabrieljablonski Feb 18, 2026
360ad59
feat: add enableCopilot prop to Editor and update ScheduledMessageMod…
gabrieljablonski Feb 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 7 additions & 7 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ jobs:
# Backend tests with parallelization
backend-tests:
<<: *defaults
parallelism: 16
parallelism: 18
steps:
- checkout
- node/install:
Expand Down Expand Up @@ -350,12 +350,12 @@ jobs:
destination: coverage

build:
<<: *defaults
steps:
- run:
name: Legacy build aggregator
command: |
echo "All main jobs passed; build job kept only for GitHub required check compatibility."
<<: *defaults
steps:
- run:
name: Legacy build aggregator
command: |
echo "All main jobs passed; build job kept only for GitHub required check compatibility."

workflows:
version: 2
Expand Down
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ AZURE_APP_SECRET=
# REMOVE_STALE_CONTACT_INBOX_JOB_STATUS=false

# REDIS_ALFRED_SIZE=10
# REDIS_VELMA_SIZE=10

# Baileys API Whatsapp provider
BAILEYS_PROVIDER_DEFAULT_CLIENT_NAME=Chatwoot
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ yarn-debug.log*
.vscode
.claude/settings.local.json
.cursor
.codex/
CLAUDE.local.md

# Histoire deployment
.netlify
.histoire
.pnpm-store/*
local/
Procfile.worktree
16 changes: 16 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

- **Setup**: `bundle install && pnpm install`
- **Run Dev**: `pnpm dev` or `overmind start -f ./Procfile.dev`
- **Seed Local Test Data**: `bundle exec rails db:seed` (quickly populates minimal data for standard feature verification)
- **Seed Search Test Data**: `bundle exec rails search:setup_test_data` (bulk fixture generation for search/performance/manual load scenarios)
- **Seed Account Sample Data (richer test data)**: `Seeders::AccountSeeder` is available as an internal utility and is exposed through Super Admin `Accounts#seed`, but can be used directly in dev workflows too:
- UI path: Super Admin → Accounts → Seed (enqueues `Internal::SeedAccountJob`).
- CLI path: `bundle exec rails runner "Internal::SeedAccountJob.perform_now(Account.find(<id>))"` (or call `Seeders::AccountSeeder.new(account: Account.find(<id>)).perform!` directly).
- **Lint JS/Vue**: `pnpm eslint` / `pnpm eslint:fix`
- **Lint Ruby**: `bundle exec rubocop -a`
- **Test JS**: `pnpm test` or `pnpm test:watch`
Expand Down Expand Up @@ -50,6 +55,13 @@
- Prefer `with_modified_env` (from spec helpers) over stubbing `ENV` directly in specs
- Specs in parallel/reloading environments: prefer comparing `error.class.name` over constant class equality when asserting raised errors

## Codex Worktree Workflow

- Use a separate git worktree + branch per task to keep changes isolated.
- Keep Codex-specific local setup under `.codex/` and use `Procfile.worktree` for worktree process orchestration.
- The setup workflow in `.codex/environments/environment.toml` should dynamically generate per-worktree DB/port values (Rails, Vite, Redis DB index) to avoid collisions.
- Start each worktree with its own Overmind socket/title so multiple instances can run at the same time.

## Commit Messages

- Prefer Conventional Commits: `type(scope): subject` (scope optional)
Expand Down Expand Up @@ -86,3 +98,7 @@ Practical checklist for any change impacting core logic or public APIs
- When renaming/moving shared code, mirror the change in `enterprise/` to prevent drift.
- Tests: Add Enterprise-specific specs under `spec/enterprise`, mirroring OSS spec layout where applicable.
- When modifying existing OSS features for Enterprise-only behavior, add an Enterprise module (via `prepend_mod_with`/`include_mod_with`) instead of editing OSS files directly—especially for policies, controllers, and services. For Enterprise-exclusive features, place code directly under `enterprise/`.

## Branding / White-labeling note

- For user-facing strings that currently contain "Chatwoot" but should adapt to branded/self-hosted installs, prefer applying `replaceInstallationName` from `shared/composables/useBranding` in the UI layer (for example tooltip and suggestion labels) instead of adding hardcoded brand-specific copy.
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,14 @@ gem 'reverse_markdown'

gem 'iso-639'
gem 'ruby-openai'
gem 'ai-agents', '>= 0.7.0'
gem 'ai-agents'

# TODO: Move this gem as a dependency of ai-agents
gem 'ruby_llm', '>= 1.8.2'
gem 'ruby_llm-schema'

gem 'cld3', '~> 3.7'

# OpenTelemetry for LLM observability
gem 'opentelemetry-sdk'
gem 'opentelemetry-exporter-otlp'
Expand Down
38 changes: 20 additions & 18 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ GEM
jbuilder (~> 2)
rails (>= 4.2, < 7.2)
selectize-rails (~> 0.6)
ai-agents (0.7.0)
ruby_llm (~> 1.8.2)
ai-agents (0.9.0)
ruby_llm (~> 1.9.1)
annotaterb (4.20.0)
activerecord (>= 6.0.0)
activesupport (>= 6.0.0)
Expand Down Expand Up @@ -186,6 +186,7 @@ GEM
byebug (11.1.3)
childprocess (5.1.0)
logger (~> 1.5)
cld3 (3.7.0)
climate_control (1.2.0)
coderay (1.1.3)
commonmarker (0.23.10)
Expand Down Expand Up @@ -297,7 +298,7 @@ GEM
railties (>= 5.0.0)
faker (3.2.0)
i18n (>= 1.8.11, < 2)
faraday (2.13.1)
faraday (2.14.1)
faraday-net_http (>= 2.0, < 3.5)
json
logger
Expand All @@ -308,12 +309,12 @@ GEM
hashie
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (3.4.0)
net-http (>= 0.5.0)
faraday-net_http (3.4.2)
net-http (~> 0.5)
faraday-net_http_persistent (2.1.0)
faraday (~> 2.5)
net-http-persistent (~> 4.0)
faraday-retry (2.2.1)
faraday-retry (2.4.0)
faraday (~> 2.0)
faraday_middleware-aws-sigv4 (1.0.1)
aws-sigv4 (~> 1.0)
Expand Down Expand Up @@ -464,7 +465,7 @@ GEM
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.13.2)
json (2.18.1)
json_refs (0.1.8)
hana
json_schemer (0.2.24)
Expand Down Expand Up @@ -539,11 +540,11 @@ GEM
net-imap
net-pop
net-smtp
marcel (1.0.4)
marcel (1.1.0)
maxminddb (0.1.22)
meta_request (0.8.3)
meta_request (0.8.5)
rack-contrib (>= 1.1, < 3)
railties (>= 3.0.0, < 8)
railties (>= 3.0.0, < 9)
method_source (1.1.0)
mime-types (3.4.1)
mime-types-data (~> 3.2015)
Expand All @@ -558,12 +559,12 @@ GEM
multi_json (1.15.0)
multi_xml (0.8.0)
bigdecimal (>= 3.1, < 5)
multipart-post (2.3.0)
multipart-post (2.4.1)
mutex_m (0.3.0)
neighbor (0.2.3)
activerecord (>= 5.2)
net-http (0.6.0)
uri
net-http (0.9.1)
uri (>= 0.11.1)
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
net-imap (0.4.20)
Expand Down Expand Up @@ -676,7 +677,7 @@ GEM
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.8.1)
rack (3.2.3)
rack (3.2.5)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-contrib (2.5.0)
Expand Down Expand Up @@ -826,7 +827,7 @@ GEM
ruby2ruby (2.5.0)
ruby_parser (~> 3.1)
sexp_processor (~> 4.6)
ruby_llm (1.8.2)
ruby_llm (1.9.2)
base64
event_stream_parser (~> 1)
faraday (>= 1.10.0)
Expand Down Expand Up @@ -970,7 +971,7 @@ GEM
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uniform_notifier (1.17.0)
uri (1.0.4)
uri (1.1.1)
uri_template (0.7.0)
valid_email2 (5.2.6)
activemodel (>= 3.2)
Expand Down Expand Up @@ -1005,7 +1006,7 @@ GEM
working_hours (1.4.1)
activesupport (>= 3.2)
tzinfo
zeitwerk (2.6.17)
zeitwerk (2.7.4)

PLATFORMS
arm64-darwin-20
Expand All @@ -1025,7 +1026,7 @@ DEPENDENCIES
administrate (>= 0.20.1)
administrate-field-active_storage (>= 1.0.3)
administrate-field-belongs_to_search (>= 0.9.0)
ai-agents (>= 0.7.0)
ai-agents
annotaterb
attr_extras
audited (~> 5.4, >= 5.4.1)
Expand All @@ -1039,6 +1040,7 @@ DEPENDENCIES
bullet
bundle-audit
byebug
cld3 (~> 3.7)
climate_control
commonmarker
csv-safe
Expand Down
2 changes: 1 addition & 1 deletion VERSION_CW
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.10.0
4.11.0
21 changes: 21 additions & 0 deletions app/builders/messages/instagram/base_message_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,25 @@ def save_story_id
return if story_reply_attributes.blank?

@message.save_story_info(story_reply_attributes)
create_story_reply_attachment(story_reply_attributes['url'])
end

def create_story_reply_attachment(story_url)
return if story_url.blank?

attachment = @message.attachments.new(
file_type: :ig_story,
account_id: @message.account_id,
external_url: story_url
)
attachment.save!
begin
attach_file(attachment, story_url)
rescue Down::Error, StandardError => e
Rails.logger.warn "Failed to download Instagram story attachment: #{e.message}"
end
@message.content_attributes[:image_type] = 'ig_story_reply'
@message.save!
end

def build_conversation
Expand Down Expand Up @@ -139,6 +158,7 @@ def message_params
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: message_type,
status: @outgoing_echo ? :delivered : :sent,
source_id: message_identifier,
content: message_content,
sender: @outgoing_echo ? nil : contact,
Expand All @@ -147,6 +167,7 @@ def message_params
}
}

params[:content_attributes][:external_echo] = true if @outgoing_echo
params[:content_attributes][:is_unsupported] = true if message_is_unsupported?
params
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
class V2::Reports::FirstResponseTimeDistributionBuilder
include DateRangeHelper

attr_reader :account, :params

def initialize(account:, params:)
@account = account
@params = params
end

def build
build_distribution
end

private

def build_distribution
results = fetch_aggregated_counts
map_to_channel_types(results)
end

def fetch_aggregated_counts
ReportingEvent
.where(account_id: account.id, name: 'first_response')
.where(range_condition)
.group(:inbox_id)
.select(
:inbox_id,
bucket_case_statements
)
end

def bucket_case_statements
<<~SQL.squish
COUNT(CASE WHEN value < 3600 THEN 1 END) AS bucket_0_1h,
COUNT(CASE WHEN value >= 3600 AND value < 14400 THEN 1 END) AS bucket_1_4h,
COUNT(CASE WHEN value >= 14400 AND value < 28800 THEN 1 END) AS bucket_4_8h,
COUNT(CASE WHEN value >= 28800 AND value < 86400 THEN 1 END) AS bucket_8_24h,
COUNT(CASE WHEN value >= 86400 THEN 1 END) AS bucket_24h_plus
SQL
end

def range_condition
range.present? ? { created_at: range } : {}
end

def inbox_channel_types
@inbox_channel_types ||= account.inboxes.pluck(:id, :channel_type).to_h
end

def map_to_channel_types(results)
results.each_with_object({}) do |row, hash|
channel_type = inbox_channel_types[row.inbox_id]
next unless channel_type

hash[channel_type] ||= empty_buckets
hash[channel_type]['0-1h'] += row.bucket_0_1h
hash[channel_type]['1-4h'] += row.bucket_1_4h
hash[channel_type]['4-8h'] += row.bucket_4_8h
hash[channel_type]['8-24h'] += row.bucket_8_24h
hash[channel_type]['24h+'] += row.bucket_24h_plus
end
end

def empty_buckets
{ '0-1h' => 0, '1-4h' => 0, '4-8h' => 0, '8-24h' => 0, '24h+' => 0 }
end
end
Loading
Loading