19 Commits

Author SHA1 Message Date
Abhinav Raut 1fc606b820 fix help center issues and reuse global i18n keys
Media:
- media rows now take the private flag from the caller instead of
  guessing it from the model type
- agent avatars upload as public so help center readers can load them,
  contact and visitor avatars stay private
- backfill existing agent avatars to public

Public pages:
- dedupe article feedback by reader IP for 24 hours
- keep the article locale when submitting feedback
- skip view count bumps from crawlers on the JSON API
- only log a search when the search itself worked

Admin and data:
- lock the help center row while creating an article, since slugs are
  unique per help center but the DB index is per collection
- block changing a collection language when it still has articles or
  sub collections
- validate article locale on every update, not only when the collection
  changes
- reject slugs with characters that break /hc/ URLs
- exclude the whole subtree from the parent picker so a collection
  cannot be moved into itself
- drop articles of a paused help center from the AI index
- take the embed generation at enqueue time so jobs commit in edit order

Editor and UI:
- show an error when a YouTube URL is not valid
- do not break the icon picker when the sprite fails to load
- ignore a slow article fetch after another article is opened

i18n: move generic strings like Published, Archived, Slug, Icon, Author,
Language, Text color and Platform to globals, and drop hint text that
just repeated the field label.
2026-08-01 22:34:12 +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 000d789ed8 shortlist tags with embeddings before asking the LLM to suggest them
Tag suggestion used to send the first 300 tags to the model, so anything past
that was invisible. Tags are now embedded into the same index as the knowledge
base, and only the tags most similar to the conversation get sent. The reconcile
loop embeds new and renamed tags and drops vectors for deleted ones, and a
provider change purges them so they are rebuilt with the new model.
2026-07-30 23:09:23 +05:30
Abhinav Raut 76db5aa851 fix help center bugs and share the embedding and article-content code
Review pass over the help center commit. Bug fixes:

- Image-only or video-only articles were rejected on save because the
  editor blanked the content when it had no text.
- An article could be moved into a collection in another help center.
  Slugs are only unique per help center, so two articles could end up
  on the same public URL.
- The article editor fired the "agent is typing" indicator into
  whatever conversation was last open, which the contact could see.
- Unsupported locales like /hc/x/zz-ZZ returned an empty 200 page with
  a canonical URL, so crawlers saw unlimited URLs. They 404 now.
- Article feedback was accepted for paused help centers.
- French pages announced lang="en".
- Editing a help center popped validation errors mid-keystroke.

Shared code that was duplicated:

- Help articles had their own copy of the snippet embedding lifecycle.
  The copy was missing the wait group, the concurrency cap and the
  generation gating, so article embeds were killed at shutdown, all hit
  the provider at once, and a slow embed could overwrite a newer edit or
  bring back a deleted article. Both content types now go through one
  embedSource lifecycle. Fingerprints are unchanged, so nothing
  re-embeds on deploy.
- Callout and collapsible CSS was written twice, once for the editor and
  once for the public page. They now share article-content.css, so the
  editor stays WYSIWYG.
- The excerpt was computed three ways. The page uses the stored one.

Less work at runtime:

- The admin tree no longer sends every article's HTML. The edit sheet
  loads the one article it needs. Publishing an article used to
  re-download the whole help center.
- Embedding reconcile no longer reads every article body every minute.
- The tree view fetched the help center twice on load.

Cleanups: one update-article route instead of two, media privacy comes
from the model type instead of a bool threaded through the handler, a
LinkListField component replaces three copies of the same repeater,
TreeNode renders the article row once, and 12 duplicate or dead i18n
keys are gone.
2026-07-25 15:50:47 +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
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 a4be8ecb95 fix review findings in the ai package
Correctness fixes from the internal/ai code review:
- gate reindex commits on the snippet row still existing, so a delete
  racing reconcile can't re-insert its embeddings forever
- cap one logical provider request at 90s across all retries, so a
  hanging provider can't stall the reply box for minutes
- return proper error envelopes from the copilot message store instead
  of raw sqlx errors that surfaced as "Error interface conversion failed"
- reject blank AI replies in the generate-reply and copilot handlers
- build GET tool URLs with url.Parse so a # in the URL doesn't swallow
  the query params
- let a blank api_key clear the stored key (masked keeps it), restoring
  a way to disable AI

Cleanups and hardening:
- share one SSRF transport between tool and provider clients instead of
  building a new transport per AI call
- copy the provider config struct instead of listing every field
- backfill temperature 0.7 for configured providers upgrading from
  releases that hardcoded it
- only pass user/assistant roles from copilot history to the provider
- reject enc:-prefixed secrets that would be stored raw and fail decrypt
- rune-safe truncation of test errors, one-line doc comments
2026-07-18 12:16:33 +05:30
Abhinav Raut a3cdd7111e remove inline OpenAI key prompt and reuse generating state for AI prompts
The reply-box AI prompts used a legacy inline dialog to set the OpenAI key, hitting a separate PUT /api/v1/ai/provider endpoint. That is now fully covered by the admin provider settings page, so drop the dialog, the endpoint, the UpdateProvider method, the set-completion-key query, and the orphaned i18n keys. With no provider configured an agent now gets the 'ask your administrator' toast instead.

Also make the inline AI prompts (Make Friendly, etc.) toggle the existing isGenerating state so the editor shows the same generating animation as Generate Reply, with a guard against overlapping runs.
2026-07-18 03:13:31 +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 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 3f7d151d33 - add getting started flow for new users
- Translate web template pass i18n dependency
- Fix colors in menu card
- Show update description if avaialble in AppUpdate component
- Remvoe i18n from settings as i18n and settings depend on each other to load initial lang.
- Clear inbox password as the update SQL query now returns the config.
- Fetch agents and inboxes from the store instead of directly fetching using axios instance.
2025-08-30 21:30:24 +05:30
Abhinav Raut 26d76c966f feat: allow setting OpenAI API KEY from the UI.
feat: new `ai:manage` permission for the same
Migrations for new role.
2025-03-01 19:40:18 +05:30
Abhinav Raut 9661e9d62d fix: send user friendly error messages for ai provider request errors. 2025-01-30 01:42:19 +05:30
Abhinav Raut 98537825e9 feat: AI clean up of agent responses, adds new ai package
- feat: full screen tiptap editor, esentially two editors that attempt to keep their state in sync.
- moves tiptap editor menu bar to to show up as a bubble once text is selected.
- Layout fixes and improvements.
- Improves /reports/overview charts and cards.
2025-01-09 05:01:33 +05:30
Abhinav Raut d8e29b569f feat: conversation snooze
feat: command menu on press ctrl + k
feat: CSAT email when a conversation is marked resolved.
feat(AI) ✨ agent message rewriting with OpenAI prompts
2025-01-08 06:34:28 +05:30