Commit Graph

350 Commits

Author SHA1 Message Date
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 c7f55e2394 guard reply-box AI prompt race and share the SSRF-safe http transport
handleAiPromptSelected now captures the conversation uuid and bails if it changed while the completion was in flight, and uses convertTextToHtml with a null guard, matching handleGenerateReply. This stops a generated prompt from landing in the wrong conversation's draft or crashing on an empty response.

Add ssrf.NewTransport that clones http.DefaultTransport (keeping proxy and connection-pool defaults) and applies the dial guard, then use it for the OIDC, AI, OpenAI, and webhook clients. The bare transports were dropping HTTP_PROXY handling and pool defaults.
2026-07-18 11:20:16 +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 e5ce8e3358 truncate long assistant descriptions in admin table 2026-07-18 02:44:19 +05:30
Abhinav Raut 0f2aef1c51 fix Groq and Together AI provider preset defaults 2026-07-18 02:15:50 +05:30
Abhinav Raut c030c9ee4b harden AI embedding and agent prompt handling, add tokenizer, and switch to a green theme
Embedding:
- Persist embedding_max_tokens. It was dropped when saving the provider config
  and always reset to the 8192 default, so self-hosted models with a smaller
  limit kept getting rejected.
- Split embedding requests to stay under the provider's item and token caps. A
  large snippet or URL import used to go in one request, hit the cap, fail, and
  get retried every minute forever.
- Skip blank chunks. An empty input string makes the API reject the whole batch.
- Add the tiktoken tokenizer (cl100k_base) with tests for token counting.

Agent prompt:
- Move contact fields, the conversation subject, and custom attributes out of
  the system prompt and into a delimited user-role block, so a crafted name or
  subject can't act as an instruction.

Frontend:
- Switch the primary and sidebar colors to a green theme.
- Show a "summarizing" info toast while the AI summary runs, and add an info
  toast variant.
- Make the snippet content box taller and the snippet dialog wider.
- Drop a hardcoded dark hover color on the scroll-to-bottom button.
2026-07-16 11:20:38 +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 afb338cb54 address PR review findings in the AI agent
Thread context through provider calls so cancelled requests stop retrying, make snippet delete and FAQ review transitions atomic, cap provider response reads, guard stale AI replies and copilot responses from overwriting newer conversation state, and stop logging raw search queries and chunk content.
2026-07-14 01:17:47 +05:30
Abhinav Raut 976c45df3e add AI provider connection test and self-heal reasoning model param errors
Reasoning models like gpt-5 reject max_tokens and non-default temperature.
The client now reads the structured 400, renames max_tokens to
max_completion_tokens or drops the bad tuning param, and retries. This
removes the old rule that only sent max_completion_tokens when reasoning
effort was set.

Provider form gets a "Test connection" button that makes one live call
with the form values and shows the provider's real error. Embedding test
also checks the returned vector length against the Dimensions field.
Add an Anthropic preset (their OpenAI-compatible endpoint) and stop
pre-filling temperature since blank is safe on every model.

Fix the shared Button loader so the spinner is visible on non-filled
variants, and polish the copilot panel: bot avatars on messages, dot
loader while thinking, cleaner input box, and tab slide-in animation.
2026-07-13 03:01:10 +05:30
Abhinav Raut e1695b7836 send AI agent confirmation question as a separate widget message
The model marks its trailing confirmation question with a [[confirm]] line.
We split that off and send it as its own chat bubble so the widget reads like
a real conversation. The customer never sees the marker. Email is left as one
combined message since separate bubbles only suit the widget.

Confirmation messages are tagged with is_confirmation in meta so they no
longer inflate the reply count in assistant stats.

Keep the widget typing indicator alive during long agent runs by
re-broadcasting every 3s, under the widget's 5s typing expiry.

Fix the AI assistant and tool edit forms to stay on the page after save.
They now only go back to the list when creating, matching the rest of the
admin forms.
2026-07-13 02:23:35 +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 dad99bf0ee render AI agent replies as markdown and fix assistant identity, handoff, and stats bugs
Replies from the AI agent are now converted from markdown to HTML with
goldmark before queueing, so bold, links, and lists render properly in the
widget, agent app, and email. The prompt now allows simple markdown. Raw
HTML in model output is escaped by goldmark, and both frontends sanitize
on render anyway.

Other fixes bundled in:
- validate avatar type and size before creating or updating an assistant,
  and roll back the assistant if the avatar upload fails after create
- return 404 from agent update and API key endpoints for AI assistant
  identity users, and hide assistants from mention and SLA user pickers
- reserve the autonomous assistant's built-in tool names so custom tools
  cannot shadow them
- apply resolve after the reply is posted so the CSAT survey follows the
  answer instead of preceding it
- unassign the assistant on handoff even when the fallback team is the
  same team
- count reopens by status category instead of status name, and exclude
  CSAT messages from the turn cap
- split oversized wrapper divs into child blocks when chunking KB HTML
  instead of truncating them
- return 404 when soft-deleting an already-deleted agent, and keep AI
  assistants (which have no email) visible in the compact users list
2026-07-12 23:12:44 +05:30
Abhinav Raut 34241f7d67 improve AI agent context and disclosure
Pass the conversation subject to the AI agent, show the AI's own expectation in the chat widget while it handles a chat, and label AI messages in continuity emails.
2026-07-12 16:34:36 +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 3ba2b8281e Merge remote-tracking branch 'origin/main' into feat/ai-agent 2026-07-12 04:50:24 +05:30
Abhinav Raut 623fa2c478 load agents directly in AgentList
Fetch agents through the API in AgentList instead of the shared users store, so the list does not depend on store state populated elsewhere.
2026-07-12 04:48:39 +05:30
Abhinav Raut c8884ec005 add AI reply-box UI and AI author links
Show an animated border on the reply box while a reply is generating, and hide generate-reply for private notes. Link an AI assistant message author to the assistants settings page for admins who can manage AI.
2026-07-12 04:48:39 +05:30
Abhinav Raut ac403791fd improve copilot reply drafts and add presets
Reply drafts are now written in first person as the agent and never offer to escalate, since a human is already handling the conversation. Add quick preset buttons to the copilot panel and reuse a shared transcript helper.
2026-07-12 04:48:29 +05:30
Abhinav Raut ef0add0fff add reasoning effort setting for providers
Add a reasoning effort field to the completion provider config, sent as-is to the model. Reasoning models such as GPT-5.x need it set to "none" to use tools. Also switch to max_completion_tokens.
2026-07-12 04:48:01 +05:30
Abhinav Raut 1343790353 restrict custom tool methods to GET and POST
Limit custom tools to GET and POST and validate the method on save. GET args now go on the query string and POST args in the body. Also stop sending the raw stored secret when its decryption fails, which would leak ciphertext and fail auth confusingly.
2026-07-12 04:47:50 +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 1bb21db623 fix prechat field removal on enter key in livechat inbox form 2026-07-11 01:14:23 +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 ddaf848579 Merge pull request #413 from abhinavxd/jwt-phone-number-comment
clarify phone_number comment in JWT payload example
2026-07-10 01:40:40 +05:30
Abhinav Raut 7754a7773c clarify phone_number comment in JWT payload example 2026-07-10 01:40:20 +05:30
Abhinav Raut 6cb8bceeb9 prefill country code for optional phone fields too in pre-chat form 2026-07-10 01:21:27 +05:30
Abhinav Raut 9c6a6c254d fix widget draft attachments, prechat phone default, and shadow tweaks
Draft attachments now keep url and disposition, so a restored draft can render its attachments correctly instead of losing them.

The prechat phone field now picks a country code from the browser locale when the field is required (falling back to US), so users are not forced to pick one manually. Non-required phone fields still start blank.

Also soften the widget iframe and launcher bubble box shadows to a lighter, tinted look.
2026-07-10 01:09:09 +05:30
Abhinav Raut e38facf750 Merge pull request #410 from abhinavxd/convo-list-sla-draft-fixes
make SLA badges react to prop changes and hide draft label on open conversation
2026-07-10 00:42:41 +05:30
Abhinav Raut 9ce7feded0 make SLA badges react to prop changes and hide draft label on open conversation 2026-07-10 00:28:36 +05:30
Abhinav Raut 0fa00cf10d Merge pull request #408 from abhinavxd/jwt-phone-country-code
accept phone country code in livechat JWT auth
2026-07-09 23:32:01 +05:30
Abhinav Raut 526d6abd50 Merge pull request #409 from abhinavxd/assign-nudge-permission-check
hide assign-to-me nudge when agent lacks assignee permission
2026-07-09 23:31:38 +05:30
Abhinav Raut fc71d30b8a accept phone country code in livechat JWT auth 2026-07-09 23:15:05 +05:30
Abhinav Raut 99492cc4fe hide assign-to-me nudge when agent lacks assignee permission 2026-07-09 22:42:28 +05:30
Abhinav Raut 2de3038d07 show attachment type in draft preview when there is no draft text 2026-07-09 22:20:50 +05:30
Abhinav Raut 8a8d372a70 use one phone field in livechat JWT and fix phone validation 2026-07-09 02:27:08 +05:30
Abhinav Raut e56e3d3b0f use distinct icons for online, away and offline status dots 2026-07-09 01:50:20 +05:30
Abhinav Raut 8f658f274d add livechat pre-chat phone field and fix default launcher logo 2026-07-09 01:49:38 +05:30
Abhinav Raut e9a7269c3f Merge pull request #404 from abhinavxd/harden-contact-create-and-drop-away-on-login
Harden contact create, drop set-away-on-login, make status dots color blind safe
2026-07-08 13:16:45 +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 4020829d66 Merge pull request #403 from abhinavxd/add-novalidate-remaining-forms
add novalidate to remaining vee-validate forms
2026-07-08 01:58:17 +05:30
Abhinav Raut 3bf95aeac1 add novalidate to remaining vee-validate forms 2026-07-08 01:44:29 +05:30
Abhinav Raut d6f44a3c61 fix livechat form submit silently failing on invalid url/number fields 2026-07-08 01:32:58 +05:30
Abhinav Raut 60ebefebdc fix edge cases in private note soft-delete 2026-07-08 01:00:24 +05:30
Abhinav Raut 43b43a82ef Merge branch 'main' into delete-private-notes 2026-07-08 00:47:28 +05:30
Abhinav Raut d696b53c80 soft-delete private notes with a tombstone instead of removing them 2026-07-08 00:43:29 +05:30
Abhinav Raut 5a826b9c14 Merge pull request #399 from abhinavxd/reply-box-per-type-drafts
Reply box per type drafts
2026-07-08 00:26:21 +05:30
Abhinav Raut adeb4eee27 swap private-note delete to a hover 3-dot menu with confirm dialog 2026-07-07 23:55:37 +05:30
Abhinav Raut 9e0c1f332d Merge branch 'main' into delete-private-notes 2026-07-07 23:37:05 +05:30
Abhinav Raut bde6f019dc Merge pull request #401 from abhinavxd/fix-dark-highlight-css-leak
fix dark-mode mention highlight leaking a yellow background
2026-07-07 23:34:51 +05:30
Abhinav Raut 8268221ea2 fix dark-mode mention highlight leaking a yellow background onto every .dark element 2026-07-07 23:26:57 +05:30