128 Commits

Author SHA1 Message Date
Abhinav Raut 9c0d242549 rebuild the command palette and add keyboard shortcuts for conversation actions 2026-09-02 15:44:27 +05:30
Abhinav Raut b136346b95 fetch only the edited view's count after saving a view 2026-09-02 01:19:50 +05:30
Abhinav Raut 2ba716cb58 Merge remote-tracking branch 'origin/main' into pr/sidebar-counts
# Conflicts:
#	frontend/apps/main/src/stores/conversation.js
#	internal/conversation/conversation.go
2026-09-02 00:45:50 +05:30
Abhinav Raut c934758ca8 cap sidebar view counts and move the count badge into a component 2026-09-01 21:18:48 +05:30
malpou 3b0d3d4415 api: accept source_id on conversation and message endpoints
Add an optional source_id to createConversationRequest and messageReq and
store it on the created contact message, mirroring the IMAP ingestion path
which is the only place source_id is otherwise populated.

BuildEmailThreadingHeaders builds every outgoing reply's References and
In-Reply-To from prior messages' source_id. A desk fed over the API, as
ours is, therefore references only its own previous sends, so the customer's
mail client has nothing of theirs to thread on and every reply arrives
looking standalone. Letting the API set source_id fixes this for any API-fed
inbox without a direct database write.

The value is normalized (whitespace and angle brackets stripped) since
source_id is stored unbracketed and the brackets are re-added when composing
the threading headers. An empty value leaves the column NULL as before, so
existing callers are unaffected.
2026-08-29 10:59:03 +02:00
Amberger Moritz 1d88ef13b1 fix: address CodeRabbit review on sidebar counts PR
Restore upstream conversation create/transcript and mobile sidebar behavior that was accidentally overwritten during cherry-pick. Import onMounted in Sidebar.vue and bound view-count queries with a 10s timeout.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-26 15:57:55 +02:00
Amberger Moritz d12c4446a6 feat: show open conversation counts in the inbox sidebar
Adds badges with the number of open conversations next to My inbox,
Mentions, Unassigned, All and each personal or shared view. "Open" means
a conversation whose status category is open, so snoozed and resolved
conversations are not counted.

Backend:
- GET /api/v1/conversations/sidebar-counts returns the counts, gated by
  the conversations:read permission. Each individual count is additionally
  gated server-side by the permission for that list, so an agent never
  receives a number for a list they cannot open.
- View counts are unioned into a single statement, so the number of views
  does not drive the number of queries.
- The list-type conditions shared by the conversation list and count
  queries are extracted into appendListTypeConditions, so both stay in
  sync on what each list type means.
- An empty team list now renders as IN (NULL) instead of the invalid
  IN (), which an agent holding a team permission without a team hit.
- The feature can be turned off via app.sidebar_counts_enabled, which
  defaults to enabled when the setting is missing.

Frontend:
- Counts refresh when switching inboxes, after the agent's own status or
  assignee changes, and from new_conversation and status-carrying
  conversation_update websocket events.
- Refreshes are throttled and share a short TTL, so bursts of incoming
  conversations collapse into a single request. Re-enabling the feature
  bypasses the TTL, because being disabled leaves a zeroed result cached.

Refs #466

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 16:58:30 +02:00
Abhinav Raut 69a6dbca81 Merge pull request #485 from abhinavxd/fix-attachment-filename-sanitization
preserve unicode in attachment filenames and escape MIME headers properly
2026-08-12 01:34:37 +05:30
Abhinav Raut e853cd39b4 preserve unicode in attachment filenames and escape MIME headers
properly

Fixes #483
2026-08-12 01:28:11 +05:30
Abhinav Raut adf6aa4270 fall back to email lookup on unknown external ID and let the form opt out of contact sync
Two contact resolution fixes in create conversation:

Under the reuse policy, an unknown external_user_id went straight to
insert. If the email belonged to an existing contact, that inserted a
duplicate contact sharing the email, since the unique email index only
covers rows without an ext_id. The ext_id lookup now falls back to the
email match before inserting. Regression test added.

The new conversation form sends name fields as a snapshot from contact
search, so a contacts:write agent submitting the form could overwrite a
contact that was renamed after the search. The form now sends
reuse_contact: true and the backend downgrades to the reuse policy when
it is set. API callers like an external CRM omit the flag and keep the
full sync/upsert behavior. The flag can only downgrade, never escalate.

Also from review: hoist the repeated password generation block into
newContactPassword, drop testutil.NewLogger and the duplicate
newTestI18n in transcript_test.go in favor of testutil.NewI18n, and
clean up stale comments.
2026-08-11 15:33:32 +05:30
Abhinav Raut 1ffcee31b7 unify contact resolution into ResolveContact with an explicit policy and add integration tests 2026-08-11 13:34:44 +05:30
Abhinav Raut f2b3bcd9b6 log enforce errors and skip password generation for existing contacts in create conversation 2026-08-11 13:00:05 +05:30
Abhinav Raut 00ec7ff934 send first message to the matched contact's stored email and pass external_user_id from the new conversation form 2026-08-11 12:21:08 +05:30
Abhinav Raut f986981b44 make no-permission contact resolution race-safe in create conversation API 2026-08-11 12:08:23 +05:30
Abhinav Raut 1112d5d0a8 tighten comment in create conversation contact handling 2026-08-11 11:56:13 +05:30
Abhinav Raut bb4fecca67 allow contact updates in create conversation API for callers with contacts:write 2026-08-11 11:53:01 +05:30
Abhinav Raut a097ca8536 fix create conversation API ignoring external_user_id for existing contacts 2026-08-11 11:36:11 +05:30
Abhinav Raut 6125f5ced0 harden AI agent knowledge base, OTP, and prompt injection defenses
Knowledge base: fold the embedding provider base URL into the snippet
fingerprint so re-pointing the provider triggers a reindex even when the
model name is unchanged. Reject empty knowledge base content. Cap
concurrent background snippet embeds at 4 and tie embedding work to the
app lifecycle context. Reindex when the embedding base URL changes, not
just the model or dimensions.

Prompt injection: neutralize << >> block delimiters in snippets,
transcripts, subjects, and contact fields so untrusted content can't
forge a boundary the model relies on.

OTP: set the verified flag inside the Lua match script so verification
is atomic. Only count codes that were actually emailed toward the resend
cap, and check the cap before sending instead of incrementing up front.

Tools: fetch only the enabled tools among the allowed IDs, and route all
registrations through one helper so custom tools can't shadow built-ins.

Agent queue: hand a dropped response job off to a human instead of
leaving the conversation assigned to the assistant with no reply.

Also let GetAllConversationMessages return every message when limit is
non-positive, populate admin forms without triggering validation, and
default the copilot name to Juno.
2026-07-22 14:56:49 +05:30
Abhinav Raut f460f6b163 expand copilot with customer-history tools, persona picker, and tag suggestions
Copilot and Generate Reply get four read-only, access-filtered tools to look up
a customer's history: list the contact's other conversations, search
conversations by exact email, fetch one conversation by reference number, and
search contacts. Tool output is marked untrusted and capped so a big response
can't blow the context window.

Copilot panel changes:
- per-agent persona picker that borrows an enabled assistant's voice, language
  and instructions without changing the tool set (stored in localStorage)
- answers render as HTML; copy, insert-into-reply, and add-as-private-note
  actions per answer
- chat history moves to its own copilot store; server history is persisted only
  after a successful reply and read back with a limit

Adds AI tag suggestions: a new endpoint suggests up to 3 existing tags for a
conversation, applied from the sidebar, never auto-applied.

Hardening and fixes:
- OIDC callback rejects non-agent users
- custom tool URLs and params schema validated on save; tool HTTP client no
  longer follows redirects; query keys pinned in the tool URL win
- GetAllConversationMessages takes an explicit limit (cap 1000)
- FAQ mining skips a candidate already pending review
- ai agent handoff records its event only after the move actually lands
- share chat message role constants; rename v2.7.0 migration to v2.6.0 and add
  the fix_grammar_spelling prompt
2026-07-19 23:33:18 +05:30
Abhinav Raut 0d755fc777 send CSAT on every resolve path
CSAT was only sent when a human resolved from the UI. AI and automation resolves go straight through UpdateConversationStatus and skipped it. Moved the send there so all paths trigger it. It is idempotent and no-ops when the contact has no email.
2026-07-12 04:47:43 +05:30
Abhinav Raut 9b5326c689 normalize email before validating create conversation request 2026-07-08 13:14:22 +05:30
Abhinav Raut 4dabc84a33 harden contact create, drop set-away-on-login, make status dots colorblind-safe 2026-07-08 12:14:36 +05:30
ahfoysal f4f8db81d9 Mark assignment notifications read on action 2026-07-06 13:32:16 +06:00
Abhinav Raut f70e488662 refactor: sanitize only the reference number for transcript filename 2026-06-07 15:22:52 +05:30
Abhinav Raut 2d81703ae2 fix: address review nits on transcript download
- sanitize Content-Disposition filename via stringutil.SanitizeFilename to neutralize header injection
- use i18n.Tc for count-aware singular/plural attachment label
- append download anchor to DOM and defer URL.revokeObjectURL for cross-browser reliability
2026-06-07 15:17:03 +05:30
Abhinav Raut eb6a195aaf feat: add conversation transcript download and CSAT in sidebar
Agents can download a full conversation as a .txt file from the
header menu. The conversation sidebar now shows the CSAT rating and
feedback once the customer responds.
2026-06-02 16:25:33 +05:30
Abhinav Raut 0654e3bd2f fix: skip empty incoming attachments; assign team/agent before running post message hooks this allows automations to see the conversations assigned agent & team. 2026-05-27 00:24:27 +05:30
Abhinav Raut 43ebed1c3a WS rewrite: per-client subscriptions and perf improvements
Targets broadcasts via per-tab list and open subs (validated at subscribe time)
instead of broadcasting message uuid to all agents. Adds frontend list typing, throttled sound,
and many perf improvements to reduce cpu memory & database queries.
Other fixes: Enforce message-to-conversation binding in handleGetMessage
2026-05-20 23:42:07 +05:30
Abhinav Raut 123b809be4 Address review feedback on bulk actions 2026-05-14 20:06:56 +05:30
Abhinav Raut 6146f28126 Add inbox channel validation in create conversation request 2026-04-14 21:28:11 +05:30
Abhinav Raut dcf45fd168 Skip already attached media in new conversation api.
Extract common logic for new message and convo api.
New helper functions to fetch multiple medias in 1 shot
2026-04-12 23:02:22 +05:30
Abhinav Raut 7524ba6c19 Add support for custom attributes to createConversationRequest
More polishes to notif panel
Add `You` for current agent in select combobox.
Remove unncesary checks for existing notifications while marking it as read.
Send oldest SLA notifcation first (update sql query)
2026-04-12 22:54:54 +05:30
Abhinav Raut 1118a6f465 Fix duplicate webhook events for conversations created via api 2026-04-02 15:10:14 +05:30
Abhinav Raut c8b717739c Add isNewConversation param to CreateContactMessage, cap page size to 500 2026-03-28 19:36:26 +05:30
Abhinav Raut 23e6a70844 Allow contacts with different external_user_ids to share email, add enrichment and remove email check from conversation matching 2026-03-28 12:29:44 +05:30
Abhinav Raut 395a4e80df Refactor ws updates to use direct json payloads instead of prop value pattern.
Add real-time agent availability status updates to widget.

Consolidate multiple WS broadcasts into single calls
2026-03-22 19:34:21 +05:30
Abhinav Raut 678239027e Refactor incoming message flow, add visitor upgrade on email reply, rate-limit livechat conversations 2026-03-11 12:19:58 +05:30
Abhinav Raut eb0a7b437d Add page visit tracking to widget
- fix duplicate initialization guard
- notification bell for agents
- Refactor and clean up dead code
- Fixes to pre chat form
- Add new column country to users table
- Update ratelimit pkg to be generic
2026-03-11 03:52:07 +05:30
Abhinav Raut 81847c7f4e i18n: fix localization issues (#231)
Not backported to main due to extensive i18n refactoring changes in this branch.
2026-02-22 15:12:00 +05:30
Abhinav Raut 7958d7e212 Merge branch 'main' into feat/live-chat-channel 2026-01-14 00:33:51 +05:30
Abhinav Raut 54d3a47b0a set defaults page size and page paginated apis 2026-01-11 16:22:48 +05:30
Abhinav Raut c8bfe36e2e feat: mark conversation as unread from context menu on conversation list 2026-01-07 11:56:51 +05:30
Abhinav Raut 1aa2d91432 feat: mentions for private notes
- mentioning an agents sends an email to the agent.
- mentioning a team sends an email to to the entire team.
2026-01-01 13:59:34 +05:30
Abhinav Raut ddddf04fb6 feat: Shared views for agents and specific teams and admin pages to manage shared views.
- Log activity for password set and role permission changes.
- Fix agent permissions not changing when a role is modified / deleted.
2025-12-27 13:57:21 +05:30
Abhinav Raut 064a73ba4b feat: add conversations:read_team_all permission to allow agents to see conversations assigned to their team, regardless of whether an agent is assigned or not.
- Allow all SLAs to be fetched without permission checks.

Ref #203
2025-12-26 19:57:45 +05:30
Abhinav Raut 22af66c44d Feat: match incoming messages to conversations using reference number in the subject.
refactor: update GetConversation method to include reference number parameter
2025-12-24 16:08:25 +05:30
Abhinav Raut 59dd3dcda7 fix: make conversation last seen timestamp and unread counts per agent, right now its global.
adds new table `conversation_last_seen`
Migrations for v0.8.5

Remove col `assignee_last_seen_at` from schema
2025-12-13 01:22:59 +05:30
Abhinav Raut 1f3ba077e9 Fix: New conversation creation should assign the team first, then the agent, since assigning the team later clears the agent assignment. 2025-12-10 01:09:35 +05:30
Abhinav Raut bf1cf025e0 Merge branch 'main' into feat/live-chat-channel 2025-10-04 13:38:42 +05:30
Abhinav Raut 210b8bb53b feat: add support for sending messages as contact
- introduce new permission `messages:write_as_contact` that needs to be set to allow this.
2025-09-13 20:55:04 +05:30