The [Galeria de Fotos] rules previously added to assistant_response_generator
only apply to the legacy V1 chat service. In V2 (captain_integration_v2),
scenario agents use scenario.liquid as their system prompt template, not
assistant_response_generator.
This adds conditional rules to scenario.liquid (matching the existing pattern
for faq_lookup and check_pix_payment) that activate for any scenario that
has the send_suite_images tool:
- Infer suite_category vs suite_number from context, no confirmation needed
- Never announce photo sending before the tool confirms images were found
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Adiciona SYSTEM_PROMPT_LEAK_PATTERNS no ResponseBuilderJob para detectar quando o LLM retornou o system prompt em vez de uma resposta ao cliente
- Filtra mensagens contaminadas do historico de conversas antes de enviar ao LLM (evita contaminacao em espiral)
- Adiciona guardrail no validate_message_content! que redireciona para handoff humano em caso de vazamento detectado
- Cria Captain::Errors::SystemPromptLeakError para tipagem do erro
- Atualiza assistant.liquid com tags INSTRUCOES_INTERNAS e REGRA CRITICA para instruir o LLM a nao reproduzir o system prompt como resposta
Replace unit+inbox combined dropdown with inbox-only select.
Add ALL_INBOXES i18n key in pt_BR and en. Units (Pix) are unrelated
to conversation reports.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implementa a página Relatórios IA com geração de análises semanais
por IA baseadas nas conversas de cada unidade/caixa de entrada.
Funcionalidades:
- Página /settings/captain/reports com dois tabs (Insights IA / Operacional)
- Botão "Gerar Análise" que enfileira job Sidekiq
- Filtro por unidade ou caixa de entrada
- Exibe insights com status (pendente/processando/concluído/falhou)
- Mostra top_topics, ai_failures e period_summary
- Estado vazio com CTA para gerar primeiro relatório
Backend:
- InsightsController com endpoints index/show/generate
- GenerateInsightsJob que processa conversas com LLM
- ConversationInsightService com chunking e merge inteligente
- Migração para adicionar inbox_id à tabela captain_conversation_insights
- Link sidebar "Relatórios IA" em /settings/captain/reports
Frontend:
- Vuex store captainReports com actions/mutations/getters
- API client CaptainReportsAPI (getInsights, generateInsight)
- i18n en e pt_BR para CAPTAIN_REPORTS.*
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Melhorias na ferramenta send_suite_images para resolver confusão entre
categoria e número de suíte:
1. **Descrições de parâmetros mais claras**
- suite_category: exemplos específicos (Hidromassagem, ALEXA, STILO)
- suite_number: apenas números (101, 102, 103) - remove exemplos confusos
2. **Instruções explícitas no system prompt**
- Seção [Galeria de Fotos] com regras claras
- Prioriza suite_category quando ambíguo
- Evita confirmações desnecessárias com cliente
3. **Mensagens de erro melhoradas**
- Sugere buscar por categoria quando busca por número falha
- Feedback mais útil para a IA
Resultado esperado:
- Cliente: "Me manda foto da suite Alexa"
- IA: busca por suite_category="Alexa" ✓ (sem pedir confirmação)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: add audio transcoding support for WhatsApp Cloud API
- Introduced `Audio::TranscodeService` to handle audio transcoding to OGG/Opus format.
- Updated `Messages::MessageBuilder` to transcode audio attachments based on `transcode_audio` parameter.
- Enhanced `WhatsappCloudService` to normalize audio content types and send voice flag for recorded audio in OGG format.
- Added utility functions for audio conversion in JavaScript.
- Updated Dockerfile to include FFmpeg for audio processing.
- Added tests for audio transcoding and WhatsApp Cloud service interactions.
* feat: enhance audio handling with transcoding support and error management
* feat: improve audio transcoding error handling and enhance audio recording features
* feat: enhance audio transcoding process and error handling for better reliability
* feat: update recorded audio handling to support boolean and array formats
* feat: Implement existing template linking for CSAT surveys
- Added functionality to link existing CSAT templates for WhatsApp channels.
- Introduced a new component for selecting existing templates.
- Updated the dashboard settings page to support template mode switching between creating new and using existing templates.
- Enhanced the CSAT template management service to handle linking existing templates and fetching available templates.
- Updated API routes to include linking and fetching available templates.
- Added tests for the new linking functionality and template availability checks.
* feat: Enhance CSAT template handling and validation across services and components
* feat: Refactor body variable extraction for CSAT templates and update related validations
* feat: Add linked_at field to CSAT template responses and update related handling
* feat: Add tests for ConversationDrop date formatting and CSAT template body variable handling
# Pull Request Template
## Description
Please include a summary of the change and issue(s) fixed. Also, mention
relevant motivation, context, and any dependencies that this change
requires.
Fixes:
The LLM call was wrapped in a transaction. This is an anti-pattern and
caused idle-connections which PG eventually terminated with
`PQconsumeInput() FATAL: terminating connection due to
idle-in-transaction timeout`
This resulted in activity messages being missing in some conversations
on captain handoff, failures queueing up for retry and captain
responding long after conversation was marked open/snoozed.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
locally and specs
## 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: Muhsin Keloth <muhsinkeramam@gmail.com>
Fixes
https://linear.app/chatwoot/issue/CW-6494/add-shopify-mandatory-compliance-webhooks-for-app-store-listing
Shopify requires all public apps to handle three GDPR compliance
webhooks before they can be listed on the App Store. Their automated
review checks for these endpoints and verifies that apps validate HMAC
signatures on incoming requests. We were failing both checks.
This PR adds a single webhook endpoint at `POST /webhooks/shopify` that
receives all three compliance events. When Shopify sends a webhook, it
signs the payload with our app's client secret and includes the
signature in the `X-Shopify-Hmac-SHA256` header. Our controller reads
the raw body, computes the expected HMAC-SHA256 digest, and rejects
mismatched requests with a 401.
Shopify identifies the event type through the `X-Shopify-Topic` header.
For `customers/data_request` and `customers/redact`, we simply
acknowledge with a 200—Chatwoot doesn't persist any Shopify customer
data. All order lookups happen as live API calls at query time. For
`shop/redact`, which Shopify sends after a merchant uninstalls the app,
we delete the integration hook for that shop domain and remove the
stored access token and configuration.
### How to test via Rails console
```
secret = GlobalConfigService.load('SHOPIFY_CLIENT_SECRET', nil)
body = '{"shop_domain":"test.myshopify.com"}'
valid_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', secret, body))
```
#### Test 1: No HMAC → 401
```
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Topic' => 'customers/data_request' }
app.response.code # => "401"
```
#### Test 2: Invalid HMAC → 401
```
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Hmac-SHA256' => 'invalid', 'X-Shopify-Topic' => 'customers/data_request' }
app.response.code # => "401"
```
#### Test 3: Valid HMAC, customers/data_request → 200
```
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Hmac-SHA256' => valid_hmac, 'X-Shopify-Topic' => 'customers/data_request' }
app.response.code # => "200"
```
#### Test 4: Valid HMAC, customers/redact → 200
```
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Hmac-SHA256' => valid_hmac, 'X-Shopify-Topic' => 'customers/redact' }
app.response.code # => "200"
```
#### Test 5: Valid HMAC, shop/redact → 200 (deletes hook)
```
# First check if a hook exists for this domain:
Integrations::Hook.where(app_id: 'shopify', reference_id: 'test.myshopify.com').count
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Hmac-SHA256' => valid_hmac, 'X-Shopify-Topic' => 'shop/redact' }
app.response.code # => "200"
```
---------
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>