Commit Graph
6527 Commits
Author SHA1 Message Date
aakashb95 63d765b14b Merge remote-tracking branch 'origin/feature/cw-7495-api' into cook/pr-15017-review 2026-07-22 23:33:29 +05:30
aakashb95 b11f2e4a0c fix(captain): revalidate FAQ matches after edits 2026-07-22 23:32:24 +05:30
aakashb95 d5a9747b81 test(captain): cover restricted FAQ suggestion sources 2026-07-22 23:30:35 +05:30
aakashb95 a8ef787878 fix(captain): allow FAQ approval across languages 2026-07-22 23:30:08 +05:30
aakashb95 d85ab03444 fix(captain): retain approved FAQ suggestion sources 2026-07-22 23:29:40 +05:30
aakashb95 b4d2fa5554 Merge remote-tracking branch 'origin/feature/cw-7495-llm' into cook/pr-14979-review 2026-07-22 23:29:08 +05:30
aakashb95 f1753f08b2 fix(captain): match approved FAQs across languages 2026-07-22 23:28:53 +05:30
aakashb95 fc82c7939e refactor(captain): clarify accessible suggestion scope 2026-07-22 23:20:41 +05:30
aakashb95 45e065eca8 docs(captain): note FAQ search indexing follow-up 2026-07-22 23:20:13 +05:30
aakashb95 1ee939a910 feat(captain): allow agents to review FAQ suggestions 2026-07-22 23:19:49 +05:30
Sivin VargheseandGitHub 3ffbaf680c Merge branch 'feature/cw-7495-api' into feature/cw-7496-frontend 2026-07-22 19:21:07 +05:30
Aakash BakhleandGitHub 837343b12d Merge branch 'feature/cw-7495-llm' into feature/cw-7495-api 2026-07-22 16:06:36 +05:30
aakashb95 9f658fbfaa fix(captain): route FAQ matching model 2026-07-22 13:41:21 +05:30
aakashb95 c183b2aa80 fix(captain): allow FAQ answers across agent messages 2026-07-22 13:36:19 +05:30
aakashb95 9a780e8716 fix(captain): serialize FAQ suggestion grouping 2026-07-22 13:14:48 +05:30
aakashb95 e48d612a55 fix(captain): preserve FAQ suggestion assistant 2026-07-22 13:11:44 +05:30
aakashb95 3662224396 fix(captain): retry failed FAQ comparisons 2026-07-22 13:10:20 +05:30
aakashb95 41082f845f test(captain): cover FAQ review behavior 2026-07-22 11:46:15 +05:30
aakashb95 d054c19770 Merge branch 'cook/faq-comparison-api' into cook/faq-suggestion-stack 2026-07-22 11:45:39 +05:30
aakashb95 961c4a8951 test(captain): cover FAQ suggestion details 2026-07-22 11:45:35 +05:30
aakashb95 9d8af17b95 Merge branch 'cook/faq-comparison-api' into cook/faq-suggestion-stack 2026-07-22 11:42:33 +05:30
aakashb95 439ac80019 test(captain): cover FAQ review through API 2026-07-22 11:42:25 +05:30
aakashb95 28fb05005d Merge remote-tracking branch 'origin/feature/cw-7495-api' into cook/faq-suggestion-stack 2026-07-22 10:40:55 +05:30
aakashb95 84c41f03d6 Merge remote-tracking branch 'origin/feature/cw-7495-llm' into cook/faq-comparison-api 2026-07-22 10:39:37 +05:30
aakashb95 a17734676c fix(captain): use mini model for FAQ matching 2026-07-22 10:37:25 +05:30
aakashb95 70a63695c3 fix(captain): fix FAQ suggestion review flows 2026-07-22 00:24:49 +05:30
aakashb95 325c306a1e Merge remote-tracking branch 'origin/feature/cw-7495-api' into codex/pr-15017-main
# Conflicts:
#	enterprise/app/controllers/api/v1/accounts/captain/assistants_controller.rb
2026-07-21 18:21:09 +05:30
Aakash BakhleandGitHub e0aa099487 Merge branch 'feature/cw-7495-llm' into feature/cw-7495-api 2026-07-21 18:17:44 +05:30
Aakash BakhleandGitHub 398b84d5de Merge branch 'develop' into feature/cw-7495-llm 2026-07-21 18:17:36 +05:30
ed30ff9c22 fix(whatsapp): allow calling a contact with no existing conversation (#15014)
## Description

Agents can now place a WhatsApp call to a contact straight from the
contacts screen, even if that contact has never messaged in. Previously
the call only worked once a conversation already existed, so a freshly
added contact would fail with "Unable to start the call. Please try
again." — the only workaround was to get the contact to message the
channel first.


## Type of change

- [ ] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

- Manually via 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

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2026-07-21 18:08:32 +05:30
aakashb95 c25aac7a99 fix(captain): scope overview suggestion counts 2026-07-21 18:07:02 +05:30
aakashb95 4c947f9260 Merge remote-tracking branch 'origin/feature/cw-7495-api' into codex/pr-15017-main 2026-07-21 18:05:21 +05:30
aakashb95 cf417a2f3e refactor(captain): share FAQ suggestion visibility scope 2026-07-21 18:04:39 +05:30
Sony MathewandGitHub 11143672a6 Merge branch 'develop' into feature/cw-7495-llm 2026-07-21 17:01:38 +05:30
7d2f01e402 feat(whatsapp): unify embedded signup feature gating (#15106)
WhatsApp embedded signup now uses
`whatsapp_embedded_signup_inbox_creation` as the single Chatwoot Cloud
rollout gate for inbox creation, proactive reconfiguration, and
disconnected inbox reauthorization. The authorization endpoint enforces
the same gate, so the UI and backend remain consistent.

Self-hosted installations keep their existing behavior.

## Things to know

- This reuses the existing feature flag; there is no migration or schema
change.
- The feature is shown as “WhatsApp Embedded Signup Flow” in feature
management.
- `whatsapp_reconfigure` remains visible and honored for self-hosted
proactive reconfiguration to preserve existing accounts. It can be
deprecated after the self-hosted dependency is removed or migrated.

## How to test

1. On Chatwoot Cloud, enable `whatsapp_embedded_signup_inbox_creation`
for an account.
2. Confirm that new WhatsApp inbox creation, proactive reconfiguration,
and disconnected inbox reauthorization are available.
3. Disable the flag and confirm those entry points are hidden and
authorization requests are rejected.
4. On self-hosted, confirm proactive reconfiguration remains controlled
by the existing `whatsapp_reconfigure` account setting.

---------

Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
2026-07-21 15:05:11 +04:00
Shivam MishraandGitHub 8c013415b8 fix: localize the captain overview summary greeting (#15108) 2026-07-21 16:29:08 +05:30
Tanmay Deep SharmaandGitHub 2144de92f2 fix(whatsapp): reopen conversation across a contact's coexistence identities (#15098)
WhatsApp contacts using coexistence are identified by more than one
source ID (a phone `wa_id` and a `BR.`/BSUID identity), so a single
contact ends up owning multiple `contact_inbox` records. The "reopen the
same conversation" feature scoped conversation reuse to a single
`contact_inbox`, so messages arriving under a different identity of the
same contact started a brand-new conversation — even with reopen enabled
— producing duplicate conversations.

This scopes reuse to the contact across all of its `contact_inbox`
records in the inbox instead of a single `contact_inbox`.

## Closes
- [CW-7651
](https://linear.app/chatwoot/issue/CW-7651/duplicate-conversations)

## How to reproduce
1. On a WhatsApp Cloud inbox with "reopen the same conversation" (lock
to single conversation) enabled.
2. Have a coexistence contact whose webhooks alternate between carrying
the phone `wa_id` and only the BSUID identity.
3. Before: each identity opens its own conversation → duplicates. After:
incoming messages reopen the contact's existing conversation regardless
of which identity the webhook carried.

## What changed
- `Whatsapp::IncomingMessageBaseService#set_conversation` now looks up
reusable conversations via `@contact.conversations.where(inbox_id:
@inbox.id)` instead of `@contact_inbox.conversations`.
- Updated existing specs to wire the conversation's `contact` to the
contact_inbox's contact, mirroring production data.
2026-07-21 16:21:40 +05:30
0e376f4fe2 feat(whatsapp-call): support BSUID callers for inbound voice calls (#14743)
## Linear Ticket
-
https://linear.app/chatwoot/issue/CW-7276/bsuid-support-to-whatsapp-voice-calling

## Description

Keeps WhatsApp voice calls in the same thread as the chat when a caller
has adopted a **WhatsApp username** and hidden their phone number.
This makes the inbound-call path BSUID-aware, reusing the same
identifier the messaging pipeline keys on so calls land on the existing
`ContactInbox`/conversation.

## Type of change

- [ ] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

-  Locally via 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

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2026-07-21 16:19:53 +05:30
Shivam MishraandGitHub 67cab7171d feat: show Captain generation path on conversation messages [CW-7484] (#15078) 2026-07-21 15:15:13 +05:30
Shivam MishraandGitHub 7a5385cc32 feat: improve captain overview loading and reuse stats for summary [CW-7610] (#15105) 2026-07-21 15:14:18 +05:30
Sivin VargheseandGitHub 920a98ccf4 fix: calls dashboard load race (#15094) 2026-07-21 15:00:08 +05:30
Aakash BakhleandGitHub ae49af354d fix: serialize multimodal Captain session content (#15096)
Captain now saves agent session records when a user message includes an
image. The saved record keeps the image URL and excludes downloaded
image bytes, so image replies no longer report a JSON serialization
error after delivery.

Fixes:
https://chatwoot-p3.sentry.io/issues/7618423184/?alert_rule_id=13673680&alert_type=issue&notification_uuid=d22a7ab9-95d6-4bba-85e0-733a28466775&project=6382945

## Root cause

RubyLLM downloads image attachments and caches the binary bytes inside
`RubyLLM::Content`. `SessionCaptureService` passed the live object to
the `run_context` JSON column. Rails then tried to encode the cached
JPEG bytes as UTF-8 and raised `JSON::GeneratorError`.

The error did not block replies, handoffs, or credit updates because
session capture rescues its own failures. The failed write meant that
Chatwoot lost the agent session record for the response.

## How to reproduce

1. Send an image to a Captain V2 assistant.
2. Let RubyLLM load the image during the model request.
3. Save the resulting conversation history in an agent session.
4. Observe the JSON encoding error when Rails reaches the cached image
bytes.

## What changed

`SessionCaptureService` now converts `RubyLLM::Content` to its JSON safe
hash before saving the current turn. The hash contains the message text
and attachment URL without the cached bytes. Other message content is
unchanged.

The focused service spec covers a cached JPEG byte payload and passes
with 12 examples. RuboCop reports no offenses in the changed service and
spec.
2026-07-21 13:04:12 +05:30
aakashb95 2485bc242d fix(captain): clean up FAQ suggestion review flow 2026-07-21 12:11:55 +05:30
d1fa8d8c2f refactor: share whatsapp/twilio template logic via @chatwoot/utils (#15001)
# Pull Request Template

## Description
Moves the WhatsApp & Twilio content-template logic to the shared
[`@chatwoot/utils`](https://github.com/chatwoot/utils)
([PR](https://github.com/chatwoot/utils/pull/62)) package so web and
mobile share one implementation. The neutral core takes the raw template
and returns `processed_params`, the same shape the web parsers already
use, so it's a drop-in with no behavior change.

- `templateHelper.js` / `URLHelper.js` → source `MEDIA_FORMATS`,
`findComponentByType`, `processVariable`, `buildTemplateParameters`,
`extractFilenameFromUrl` from the package
- `inboxes.js` → filters with shared `isSendableTemplate`
- `WhatsAppTemplateParser.vue` / `ContentTemplateParser.vue` →
`isFormInvalid` and Twilio media helpers now use the shared
`isWhatsAppComplete` / `isTwilioComplete` / `applyTwilioMediaFilename`

> Depends on the `@chatwoot/utils` release adding the shared template
API, bump `package.json` from `^0.0.55` to the published version before
merge.

Fixes
[CW-7540](https://linear.app/chatwoot/issue/CW-7540/web-templates-integration-with-utils)

## Type of change

- [x] Breaking change (Refactor)

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
2026-07-21 10:08:23 +04:00
71fffdd2b9 fix: rate limit widget conversation transcript API (#15085)
## Description

The widget conversation transcript endpoint (`POST
/api/v1/widget/conversations/transcript`) has no rate limit. Every other
comparable endpoint does: the agent-facing transcript API and the widget
conversation-create and contact-update endpoints are all throttled. This
gap lets a single client trigger a large burst of transcript emails from
one conversation.

This adds an IP-based throttle (5 requests/hour) for the endpoint,
placed inside the existing widget-API throttle block so it inherits the
`ENABLE_RACK_ATTACK_WIDGET_API` opt-out used by embedded/iframe clients.
The limit is generous for legitimate use (a visitor emailing themselves
a transcript) while stopping abusive loops. Throttled requests get the
standard 429 the widget already handles.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

`config/initializers/rack_attack.rb` throttles have no existing specs in
this file, so this follows the established convention (no new spec).
Verified `ruby -c` and `rubocop` pass on the file. The new throttle
mirrors the sibling widget throttles directly above it (same IP key,
path guard, and structure).

## 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

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
2026-07-20 16:09:29 -07:00
Sojan JoseandGitHub eae9841eb4 fix: restore token access to account APIs (#15088)
Token-authenticated requests to Agent Bots, Labels, and affected Captain
endpoints return normal responses again. The regression was caused by
duplicate `current_account` callbacks in subclasses moving account
resolution behind the API entitlement check, leaving `Current.account`
unset.

## Closes

- https://linear.app/chatwoot/issue/CW-7641/5xx-errors-in-agent-bot-apis

## How to reproduce

1. Send `GET /api/v1/accounts/:account_id/agent_bots` with a valid
administrator API access token.
2. Observe a `500` from `validate_token_api_access` because
`Current.account` is `nil`.
3. With this change, account resolution runs in the base-controller
order and the request succeeds.

## What changed

- Removed redundant `current_account` callbacks from account-scoped
controllers that already inherit the callback from
`Api::V1::Accounts::BaseController`.
- Kept the standalone direct-upload controller callback unchanged.
- Added regression coverage for administrator API-token access to Agent
Bots.
2026-07-20 15:28:33 -07:00
Aakash BakhleandGitHub 412319462a Merge branch 'develop' into feature/cw-7495-llm 2026-07-20 22:36:29 +05:30
160732c07d fix: rate limit agent management APIs (#15081)
# Pull Request Template


Bring  agent create and delete requests under rack attack throttling

Related to https://linear.app/chatwoot/issue/CW-7637


Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
2026-07-20 20:41:39 +05:30
Sivin VargheseandGitHub 7a299307b8 fix: apply installation name to sender name preview (#15076) 2026-07-20 19:28:58 +05:30
Sivin VargheseandGitHub 08f49f5896 fix: prevent channel list crash on hard reload (#15074) 2026-07-20 19:28:48 +05:30