Commit Graph

145 Commits

Author SHA1 Message Date
Marc Vila 08a12801d9 feat: add separate permission for private conversation notes 2026-08-30 10:22:09 +02:00
Abhinav Raut 3218f79169 fix away agent count, index resolved_at and clamp report days 2026-08-21 04:54:49 +05:30
Abhinav Raut 51f7bace9e fix and speed up the report overview queries
The resolved conversations chart filtered on created_at but grouped by resolved_at, so anything created before the window never showed up, and the tag list dropped tags whose conversations were all older than the window. Dropped the message volume COUNT(DISTINCT) and three of the four users table scans, and added the created_at indexes these queries need. The Pending tile is now labelled Awaiting First Reply since it counts conversations with no agent reply yet.
2026-08-21 04:19:03 +05:30
Abhinav Raut 0c99b015cd fix missing logo in emails on a fresh install 2026-08-17 18:10:13 +05:30
Abhinav Raut 5735214d1a Merge remote-tracking branch 'origin/main' into worktree-gdpr
# Conflicts:
#	cmd/upgrade.go
#	frontend/apps/main/src/views/contact/ContactDetailView.vue
#	i18n/en-US.json
#	internal/media/queries.sql
#	internal/migrations/v2.6.0.go
#	internal/user/contact.go
#	schema.sql
2026-08-15 13:17:49 +05:30
Abhinav Raut 2183e8140d move help center branding columns into theme and ship Instrument Sans 2026-08-15 02:22:49 +05:30
Abhinav Raut 7ea3042d11 remove archived article status and fix help center tree loading 2026-08-14 21:41:52 +05:30
Abhinav Raut 6aa887f730 add a locale picker and weighted full-text search to the help center
Locales are now a fixed whitelist (internal/helpcenter/locales.go) served via
/api/v1/help-centers/locales, and the admin form uses a combobox instead of a
free-text code input. Validation checks the whitelist instead of a BCP-47 regex.

Search: help_articles gets a generated search_tsv column with per-locale
stemming via a new help_article_search_config() SQL function, weighted
title/excerpt/body (A/B/C), a GIN index, and ts_rank length normalization so
long articles don't win by repetition. Body text is capped at 100K chars to
stay under the 1MB tsvector limit. The trigram ILIKE fallback stays for CJK
and infix matches.

The v2.8.0 migration is consolidated into one-shot CREATE TABLEs matching
schema.sql since the release isn't out yet.
2026-08-14 04:15:32 +05:30
Abhinav Raut 5027fe23a2 fix a batch of help center review findings
Backend:
- log LinkHelpArticleMedia errors on article create/update
- trim a trailing slash off the root URL in PublicURL
- run uniqueCollectionSlug inside the caller's transaction
- update-article sets author_id directly so clearing the author works
- apply the help_centers template CHECK as a named constraint so it
  also lands on installs that ran an earlier build of the migration

Frontend:
- new editor tables get the email-safe inline styles by default
- replace deprecated word-wrap/word-break with overflow-wrap
- reject a default locale that is not in the allowed locales
- show an error when the icon picker sprite fails to load
- name the locale remove buttons and keep classic-only header
  fields mounted with v-show

Docs template:
- move the collection link out of <summary>, toggle is its own button
- use logical CSS properties so the sidebar nav renders in RTL
- fix text-rendering keyword casing
2026-08-13 17:12:12 +05:30
Abhinav Raut 6083d2f098 serve help centers at the root of a custom domain
public_url is now custom_domain. It only accepts a bare origin, no path or query.

When a help center has a custom domain, its pages live at /{locale} on that domain. Requests on that host fall through to the router's not-found handler, which prepends /hc/{slug} and re-runs the router once, so nothing about the routes changes. All page links come from a new BasePath value, which is empty-prefixed on a custom domain and /hc/{slug} on the app host. Hits on the old /hc/{slug} paths 301 to the custom domain so there is one canonical address per page.

Redirects now send a path-only Location built through fasthttp's URI encoder. ctx.Redirect would turn it into an absolute http:// URL, since nginx forwards to the app over plain HTTP, and a raw path would let /\host in the URL read as another site.

robots.txt on a custom domain also disallows the search pages.
2026-08-13 01:27:22 +05:30
Abhinav Raut 048eb1acd5 add page template setting for help center with docs and classic layouts
Help centers now have a template setting with two layouts. Classic is
the default: the existing hero banner, large search bar and collection
cards. Docs is new: sticky top bar, collections sidebar on every page,
command palette search (Ctrl+K) with a no-JS fallback form, and an
on-page table of contents.

Backend adds a template column (default 'classic', checked to the two
values), threads it through insert/update queries and the request
validator, and resolves page template names as '<template>-<page>' when
rendering public pages. Docs pages also get the published tree for the
sidebar.

Public templates are restructured so each layout is fully isolated and
a third template is just a new directory plus its CSS file:
web-templates/help/shared/ holds the head, announcement, lang switcher,
icons and shared JS; help/classic/ and help/docs/ each hold their own
layout and five pages. The classic stylesheet is renamed to
help-center-classic.css so the head links help-center-<template>.css
directly. Template parsing now globs the help/ subdirectories too.

The admin create and edit forms get a template picker, and classic-only
styling fields (header background, collection layout, card icons) are
hidden when docs is selected.
2026-08-09 17:40:52 +05:30
Abhinav Raut 353558ce9b add a public URL per help center and fix code review issues
Each help center gets an optional public_url. When set, canonical links, hreflang
alternates, og:url, the sitemap and robots.txt point at that host instead of the
app root URL. Stored media URLs are rewritten to root-relative paths so logos,
favicons, header images and article images resolve on whichever host serves the
page. The URL is validated on both the form and the backend, and trailing slashes
are trimmed.

Also fixes issues found in code review:

- the help center template used a locale link as the data root inside the
  alternates loop, which broke every public page on a help center with two or
  more locales
- creating an article posted the clicked collection id, so changing the
  collection in the sheet filed the article in the wrong place
- the collection field's error message was hidden when the locale had no
  collections, so Create did nothing with no error shown
- switching the header type or unticking popular articles dropped those theme
  values on save, because vee-validate unsets fields that unmount
- closing a new collection sheet with Esc left the parent id set, so the next
  collection you edited got re-parented
- a table row with th labels in the first column was treated as a header row
  when preparing article content for embeddings
- the img tag regex stopped at a > inside an attribute value and injected
  loading/decoding in the middle of an attribute
2026-08-05 01:10:31 +05:30
Abhinav Raut 5e320b8e4c add article preview, announcement banner, and author selection to help center
The customize page preview now has a page selector (landing or a sample
article) and a device selector (desktop, tablet, mobile) with the frame
rendered at real device width and scaled to fit, wrapped in a browser
chrome mockup showing the live title, favicon and URL.

Themes gain an announcement banner shown above the header, a popular
articles section toggle with a custom label, and an icon tile option for
collection cards. Theme normalization now starts from defaults instead
of zero values.

Articles get a selectable author (validated as an agent or AI assistant)
separate from the new created_by column that records who created it. A
soft-deleted author no longer blocks saving an existing article.
2026-08-04 01:53:26 +05:30
Abhinav Raut 9cac787f30 fix preview sources and reindex articles when a collection changes
The assistant test tab looked up every search hit as a snippet, so a help
article showed an unrelated snippet's title or vanished from the list. Also
drops the help center view count, which nothing ever read.
2026-08-02 19:08:42 +05:30
Abhinav Raut 6aa221a4fc merge origin/main into help-center
Resolved the AI embedding conflicts by keeping this branch's generic embedSource path and layering main's tag indexing on top. Search now covers snippets and help articles, tags stay excluded. Dropped SetContentID since main's link-message-media query stamps content_id itself.
2026-08-01 11:47:59 +05:30
Abhinav Raut 7b69690394 add SEO, localization, and drag reordering to the help center
Public pages were missing most of what a search engine needs. They now serve
a sitemap index at /sitemap.xml plus a robots.txt, and every page carries a
canonical URL, hreflang alternates, an x-default, and JSON-LD. Article pages
also send published and modified times. Search pages and the markdown view
are marked noindex. Requests from bots no longer bump the view counters, so
the insights numbers reflect real readers. Offscreen images in articles are
lazy loaded.

Public page text is now translated per locale instead of always using the
desk language, and pages with more than one translation show a language
switcher.

In the admin, collections and articles can be dragged to reorder, and an
article can be dragged into a different collection. Both save through new
sort order endpoints. Collections can have a lucide icon, picked from a new
icon picker backed by a sprite sheet. The landing page has layout options
for card grid or one per row, cards per row, and which details to show.

The help center list is a data table now, so HelpCenterCard is gone. The
tree page moved into AdminSplitLayout, its actions sit on their own row
below the breadcrumb, and it gained visit site and expand/collapse all.
2026-08-01 10:32:52 +05:30
Abhinav Raut f2edc74e18 hardcode copilot name and tidy up AI admin copy 2026-07-31 03:50:39 +05:30
Abhinav Raut 38983f173d Merge remote-tracking branch 'origin/main' into help-center
# Conflicts:
#	frontend/apps/main/src/features/contact/ContactNotes.vue
2026-07-30 01:15:21 +05:30
Abhinav Raut fb4d665ca7 Merge pull request #421 from abhinavxd/feat/ai-agent
Feat/ai agent
2026-07-30 01:07:34 +05:30
Abhinav Raut baff30fc28 add help center with public pages, admin management and AI article indexing
Agents can now publish a public knowledge base per help center.

Backend:
- new internal/helpcenter package for help centers, collections and articles
- admin JSON API behind a new help_center:manage permission
- public /hc/{slug} pages, JSON API, search and sitemap, all rate limited
- migration v2.7.0 for the new tables

AI:
- published articles with the AI flag on get embedded, so the agent answers
  from them. Unpublishing or clearing the flag removes the embeddings.

Media:
- media rows now carry a private flag. Help article images are public so the
  public pages can serve them without auth, and only agents with
  help_center:manage can upload them.

Frontend:
- admin help center list, tree view and article editor
- the old TextEditor is split into ArticleEditor and ConversationEditor over a
  shared useTextEditor composable, so article-only tools like callouts,
  collapsibles and YouTube embeds stay out of the reply box
2026-07-25 14:19:38 +05:30
chahat-101 d2a548efa8 seed default business hours and SLA policy on install 2026-07-22 17:19:21 +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 bfc3b6e196 gate AI custom tools behind email OTP verification
Custom tools can now require a verified contact before they run. This gives tools a trustworthy signal about who the customer is without relying on DMARC or the JWT login.

ai_tools gets a requires_verification column, defaulting to true (fail-closed). A flagged tool is blocked in httpTool.Execute until the conversation is verified, and every tool call now carries an X-Libredesk-Contact-Verified header so tool authors can tell an OTP-verified contact from a self-claimed one.

The AI agent gets three native tools: send_email_verification emails a 6-digit code out of band, check_email_verification confirms it, and set_contact_email lets an anonymous visitor add an email to send the code to. Codes and the verified window live in Redis, scoped per conversation, with attempt and resend caps. JWT livechat contacts stay trusted without OTP; email contacts and visitors verify by code.

The tool admin form gets a "require verified contact" toggle (default on) with a confirm dialog when turning it off.
2026-07-20 17:48:41 +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 8cdfe861e8 add AI snippet URL import, conversation summarize, and assistant reply languages
Snippet import: new "Import from URL" flow fetches a page and stores its
readable content as a snippet. Extraction uses the mackee/go-readability
library (Mozilla Readability port) and outputs Markdown, so nav/footer
boilerplate is dropped. The snippet list shows the source, and the edit
dialog is now wider with a taller content box.

Summarize: new "Summarize with AI" action on a conversation calls the AI and
adds the result as a private note. It shows an info toast right away so the
user knows it started, since the call can take a few seconds. This adds an
"info" toast variant that any feature can use.

Assistant languages: assistants can be given a list of allowed reply
languages. The assistant replies in the customer's language when it is one of
them, otherwise it falls back to the first. The preview also lists the
knowledge sources it used.

Cleanup: replace hardcoded gray/zinc/white colors with theme tokens
(text-muted-foreground, text-foreground, bg-accent) across several components.
2026-07-16 01:39:45 +05:30
Abhinav Raut bfbcd0a885 add contact deletion and data export for GDPR compliance
First part of GDPR support (#244): the right to erasure and the right
to access.

- DELETE /api/v1/contacts/{id} permanently deletes a contact or
  visitor. Conversations, messages, notes, and participants go with it
  via DB cascades. The avatar file is removed too.
- GET /api/v1/contacts/{id}/export downloads a JSON file with
  everything stored about the contact: profile, custom attributes, and
  all conversations with their messages. Private notes stay internal.
- Both actions are gated by new permissions (contacts:delete,
  contacts:export), granted to Admin in migration v2.6.0, and recorded
  in the activity log with actor and IP.
- Contact page gets Export data and Delete contact buttons, with a
  confirm dialog for delete.
- The unlinked media cleaner now also removes attachment files whose
  message no longer exists. Before this, deleting a conversation left
  its attachments on disk forever.
2026-07-14 01:30:59 +05:30
Abhinav Raut 85a38699da restrict copilot to built-in tools and add per-assistant handoff toggle
Custom tools are HTTP calls with no read-only guarantee, so a mutating tool
could fire from copilot chat or while drafting a reply. Copilot and
generate-reply now get only the built-in knowledge search, and custom tools
stay exclusive to assistants where admins pick them explicitly. This matches
Intercom, Chatwoot, and Freshdesk. The contact identity headers now flow only
on the assistant path.

Also:
- new "Offer handoff to a human" switch on assistants (default on). When off,
  the hand_off_to_human tool is not registered and the prompt tells the
  assistant to say it cannot help instead of offering a human. Safety exits
  (error, max turns, other participant) still unassign as before.
- workspace admin instructions from the AI config no longer leak into the
  customer-facing assistant prompt.
- copilot and reply-draft prompts now treat conversation text and tool
  outputs as untrusted data.
2026-07-13 01:57:44 +05:30
Abhinav Raut a7288e11b0 add AI assistant expectation message shown in the livechat widget 2026-07-12 13:29:59 +05:30
Abhinav Raut 7186c95e40 add WIP autonomous AI agent that replies to assigned conversations
Medium (title + short body):
add WIP autonomous AI agent

New internal/aiagent package runs AI assistants that reply to customers
on conversations assigned to them, grounded on a knowledge base. Also mines
resolved conversations for FAQ suggestions. Adds admin UI and the v2.7.0
schema. Still work in progress.
2026-07-11 04:54:49 +05:30
Abhinav Raut 0ecd783941 add AI copilot backed by a knowledge base and custom tools
Adds two agent-facing AI features: a copilot chat panel in the conversation
sidebar and a generate-reply button in the reply box. Both run an agentic
tool-calling loop whose first tool searches the knowledge base.

Snippets are chunked and embedded on save, then searched in memory with
brute-force cosine similarity (no pgvector). Providers are split into completion
and embedding types. Both are OpenAI-compatible and the API key is encrypted at
rest. Admins can also register custom HTTP tools the model can call.

A new admin AI settings page covers provider config, snippets, and tools. The
v2.6.0 migration and schema add the ai_knowledge_base, embeddings, and ai_tools
tables plus the ai_providers type column.
2026-07-10 15:51:27 +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
Abhinav Raut 186502a4f8 open the reply/note tab matching the conversation's last-used draft 2026-07-07 22:57:50 +05:30
Abhinav Raut ec3938c365 save reply and private note drafts separately per type 2026-07-03 03:37:09 +05:30
Abhinav Raut 62a1fc836e add livechat widget preview and misc improvements
Livechat:
- Add a live widget preview that renders in the inbox settings help rail.
- Warn when the primary or header colors are too close to the background.
- Require gradient/image background values and notice banner text when enabled.
- Show a default launcher logo in the widget when none is set, matching the preview.
- Drop the Beta badge from the livechat channel.

General settings:
- Add "set agents away on login". Agents log in as away and must go online manually.
- Add "show subject in conversation list" toggle.

Other:
- Add a keyboard shortcuts dialog from the user menu.
- Show a "snoozed until" badge on snoozed conversations.
- Contact search now matches emails partially instead of exact.
- Give outgoing message bubbles a secondary background.
2026-07-03 02:34:42 +05:30
Abhinav Raut 1fccfceb4c feat: add customisable From display name template for outgoing emails
Adds a from_name_template field to email inboxes. When set and the sender is an agent, the template renders with agent and inbox variables and becomes the From display name; the inbox email address is unchanged. Any render failure, or a system-user sender, falls back to the plain inbox From so a misconfigured template never blocks delivery.

Variables: .Agent.FirstName, .Agent.LastName, .Agent.FullName, .Inbox.Name (the inbox name). Based on #249.

Co-authored-by: josephsellers <6892567+josephsellers@users.noreply.github.com>
2026-06-07 14:20:56 +05:30
Abhinav Raut edc82e9e55 refactor: drop Casbin, centralize agent cache invalidation, dedupe pending SLAs 2026-05-21 21:24:42 +05:30
Abhinav Raut 41ef3fed90 fix conversation list fetch races and scope notification sound to current list and only play when tab is not open like whatsapp.
Remove unused fetch participants API.
Fix incorrect default language in schema.sql
Show correct draft preview when text editor only has
2026-05-18 18:40:25 +05:30
Abhinav Raut 1012949539 Merge remote-tracking branch 'origin/main' into category-statuses 2026-04-21 21:28:07 +05:30
Abhinav Raut 06b36b74fd Add prompt-to-tag-on-reply inbox setting 2026-04-21 20:24:19 +05:30
Abhinav Raut aa9e4cdae2 feat: add conversation status categories and update related logic.
- Introduced a new category field for conversation statuses to classify them as 'open', 'waiting', or 'resolved'. Which allows max assignment limits in team to work with custom statuses as well.
2026-04-21 16:55:09 +05:30
Abhinav Raut 28bdb89e8d Update csat request email template
Update csat webpage again..
2026-04-14 22:14:39 +05:30
Abhinav Raut 9d91bb8636 Send only one CSAT per conversation 2026-04-13 13:56:15 +05:30
Abhinav Raut 8846ede8c7 standardize project name to lowercase "libredesk" across documentation, UI, and codebase.
Vue router console warning fixes
2026-04-10 16:33:27 +05:30
Abhinav Raut 88a58c0f3b Custom static directory support, self hosted installations can pass static dir path to override csat and other web-templates, js and css files. Allowing instances to be fully customizable.
- New meta JSONB field in CSAT responses to capture random adhoc data as that might be needed for custom CSAT pages.
- Add support prefilled rating for csat pages picked from query param
2026-04-09 00:04:00 +05:30
Abhinav Raut 000fd83b30 Add uuid column to inboxes table to use as widget public identifiers
Auto focus on form inputs in widget
Use defer widget in widget installation script
2026-04-03 02:34:07 +05:30
Abhinav Raut 13db8d2843 Fix command palette macros not working due to previous variable rename causing issues.
Improve sidebar navigation to retain open conversation

Fix notification when disabled app still tries sending emails.
2026-03-30 18:51:23 +05:30
Abhinav Raut b1bef238cf Feat: context links, context links allow admins to set urls to external system from sidebar with encrypted payload in query parameters. 2026-03-30 16:00:37 +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 8fd05667ac Improve sidebar semantics, form label hierarchy, and translate remaining hardcoded strings 2026-03-25 00:47:18 +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