Commit Graph

982 Commits

Author SHA1 Message Date
Abhinav Raut c25452a152 remove redundant snippet URL import hint 2026-07-31 03:53:37 +05:30
Abhinav Raut f2edc74e18 hardcode copilot name and tidy up AI admin copy 2026-07-31 03:50:39 +05:30
Abhinav Raut 79da9e8988 Fix live chat image thumbnails 2026-07-31 01:05:32 +05:30
Abhinav Raut 16b99069b9 render the generated reply as html instead of showing raw markdown 2026-07-30 16:52:56 +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 5e972d1dc5 point each AI admin page's docs link at its section anchor 2026-07-30 00:49:48 +05:30
Abhinav Raut bbcc92f2f3 add an aria-label to the channel icon in the conversation list 2026-07-30 00:47:29 +05:30
Abhinav Raut 276f19de0a show enabled state as a status badge and stop making checkbox fields required 2026-07-30 00:25:56 +05:30
Abhinav Raut 82f29f2789 fix Vue onUnmounted warning on the auth pages 2026-07-30 00:02:35 +05:30
dependabot[bot] 349e695fad build(deps-dev): bump postcss from 8.5.10 to 8.5.18 in /frontend
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.10 to 8.5.18.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.10...8.5.18)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.18
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-26 21:30:33 +00:00
Abhinav Raut 71fb197826 bump the default completion model off gpt-4o-mini 2026-07-27 02:39:57 +05:30
Abhinav Raut fd21893ebc cache AI assistants in a store instead of refetching on every copilot panel mount 2026-07-26 23:06:54 +05:30
Abhinav Raut b569777cf6 match email quote classes exactly and enforce password length on set password 2026-07-25 21:46:17 +05:30
Abhinav Raut 6b8a0f9521 fix content loss in knowledge base chunking and harden AI agent limits
Final review pass before taking the AI agent branch live.

Knowledge base:
- Text not wrapped in a block tag was never collected, so prose around a
  table or list never reached the index. The assistant answered "no
  relevant information" for questions the snippet covered.
- Blocks over the token limit were truncated and the remainder dropped. They
  are split into several chunks now.
- Trimming an oversized block ran one rune at a time and re-tokenized the
  whole string each step. A large table took minutes. It uses a binary
  search now.
- Overlap text was not escaped, so a sentence containing markup swallowed
  the rest of the chunk.
- SVG and template text no longer reaches the index.

AI agent:
- Verification codes are capped per address and per conversation. The cap
  was per conversation only, so a customer correcting a mistyped email was
  told to check an inbox that never got a code.
- Livechat verification sends synchronously. A queued send returned nil even
  when SMTP failed, so a failure counted as a sent code.
- Queued jobs drain on shutdown and hand off to a human instead of being
  dropped with no reply.
- Deleting an assistant no longer moves resolved and closed conversations
  into the fallback team.
- Image decode is capped at 25 MP. The old bound allowed a 400 MB decode per
  attachment.

Auth and admin:
- A blank OIDC client secret no longer overwrites the stored one. Blank id
  or secret is rejected instead.
- OIDC token exchange uses the SSRF guarded client with a timeout.
- Renaming a tool auth header no longer attaches the secret of whichever row
  now sits at that position.
- Clearing embedding dimensions no longer refills 1536 on the next load,
  which pushed a wrong value to the provider on the next save.
- Copilot conversation lookups filter by access before capping at 10.
2026-07-25 03:52:32 +05:30
Abhinav Raut 9f8f10ae74 drop the unenforced-rules section from the design doc 2026-07-25 00:55:29 +05:30
Abhinav Raut 23938a187a rename the design doc to DESIGN.md and resync it with the code
The surfaces section still described the old gray sidebar and the pre-swap
canvas values. Sidebar background now matches the app background in both
themes, so the tier diagram was wrong.

Also documents what the code already does: the .box, .sidebar-section-label
and .link-style utilities, the badge success variant, the variant prop on
AlertDialogAction, the hover-reveal pattern for table row actions, and the
rule that every non-submit button in a form needs type="button". Trimmed the
prose throughout.
2026-07-25 00:53:43 +05:30
Abhinav Raut d8b73c5877 retheme UI to the green palette and fix form and search bugs
Colors: the brand color moves from indigo to green in both themes, and the
sidebar, tooltip, card and link styles follow it. Three new tokens replace
hardcoded values: foreground-lighter for idle sidebar items, warning-600 for
warning text that needs 4.5:1 contrast on light backgrounds, and link for
anchors inside rendered email content. DESIGN_SYSTEM.md now lists the real
values from main.scss instead of the old indigo ones.

Forms: every button inside a form that is not the submit button now has an
explicit type. Without it the browser treats it as a submit button, so
clicking Cancel on a contact note posted an empty note, "New holiday" saved
the whole business-hours form, and pressing Enter in an SLA field deleted the
first alert row. The login page also highlights an empty password field on a
failed submit, which a broken condition prevented before, and the two
password fields on the set-password page get their own show and hide toggles.

Search: conversation and contact search now drop responses from an older
query, so clearing the box no longer repopulates the list with stale results.

Permissions: /api/v1/ai/summarize now needs messages:write, since it writes a
private note, and the menu item is hidden for agents without it.
2026-07-25 00:49:26 +05:30
Abhinav Raut 712fcbb642 return clear 400 errors for invalid AI tool and snippet input
An invalid custom tool name (bad characters or over 64 chars) used to
skip validation, hit the database check constraint, and return a 500.
The name format and length are now checked up front and return a 400
with the name hint, matching how the reserved-name check already works.
Bad tool URLs and parameter JSON now return their own specific message
instead of a generic "Something went wrong". Snippet create and update
now reject an empty title, like they already do for empty content.
Adds a unit test covering the tool validation cases.
2026-07-23 10:00:19 +05:30
Abhinav Raut 7c2e19b763 style sidebar section headers as labels and add design system doc 2026-07-23 03:52:05 +05:30
Abhinav Raut b8b2b6c9af standardize frontend design system, fix form validation, and skip continuity/CSAT messages in AI context
Design system:
- add semantic success and warning tokens (light + dark) and wire them into Tailwind
- move all hardcoded status colors (green/amber/red) onto tokens across main app and widget; keep file-type icons as identity colors
- give light mode real surface depth: gray chrome sidebars vs white content, deeper canvas gutter, crisper borders, wider gray spread so selected/hover states show
- unify radius (cards rounded-lg, controls rounded-md) and elevation (card shadow-sm, menu shadow-md)
- make reports overview colors uniform: neutral numbers, green met / red breached
- normalize the two page-title heading outliers to text-xl font-semibold

Form fixes:
- require content on AI snippets
- only include non-checkbox prechat fields when they have a value

AI context:
- skip continuity and CSAT messages in AI history, mining, and previous-conversation tools
2026-07-23 03:37:33 +05:30
Abhinav Raut 74813b2e17 reveal admin table row actions on hover and switch to vertical menu, add AI tool enable toggle 2026-07-22 22:04:00 +05:30
Abhinav Raut 40f8284e98 strip quoted reply chains from AI agent message context
When the agent built conversation history and mined FAQs, it used the
raw message text, which included the full quoted reply chain from every
email. That wasted tokens and confused the model with old back-and-forth.

Add emailquote.go to strip quoted blocks. HTML messages get their quote
containers pruned (gmail, yahoo, protonmail, outlook markers, and
blockquotes); plain text gets trailing ">" lines and "On ... wrote:" /
"Original Message" markers trimmed. If stripping leaves nothing (a
quote-only reply or forward), we fall back to the full text so the
message is not dropped. Add the matching protonmail_quote selector to
the frontend hide-quoted-text styles so the two stay in sync.

Also fix knowledge base chunking: plain text with no block structure was
kept as one chunk and could overflow the model limit. It now packs into
size-bound pieces on sentence boundaries, and oversized atomic blocks are
flushed and truncated on their own so they can never sneak through.
2026-07-22 21:22:35 +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 cf629f34d7 only include checked checkbox fields in prechat form submission 2026-07-22 01:31:37 +05:30
Abhinav Raut 83f9bb5828 show a BETA badge on the AI admin nav item
Adds a badge prop to nav items and renders it next to the collapsible
group title. The AI section now shows BETA in the sidebar.
2026-07-22 01:20:21 +05:30
Abhinav Raut ae44e7f4db expand AI tool context and let contacts correct their email
Custom HTTP tools now get more identity context. Each call sends the
contact id, contact type, conversation UUID, and inbox id as headers,
and the contact email is read live per call instead of snapshotting it
at run start.

set_contact_email is no longer blocked once an email is known. A
customer who gives a different email (for example after their account
could not be found) can now correct it. Changing the email clears the
verification flag and any pending code first, so a failed clear can
never leave the conversation verified against an unproven address. The
prompt and tool descriptions were updated to guide this flow.

Also fixes some widget and admin UI issues: prechat form validation
now handles required numbers, checkboxes, and links correctly; tool
header rows keep stable keys so removing a row does not shuffle inputs;
the verification toggle uses form state directly; and the livechat
inbox form shows the inbox UUID with a copy button.
2026-07-22 00:58:41 +05:30
Abhinav Raut ea03339b0d address CodeRabbit review findings in the AI agent
Drop the deprecated webhook.allowed_hosts fallback from the SSRF setup.
It appended hostnames to allowed_cidrs, which only parses CIDRs, so those
entries were silently dropped anyway. Read the [ssrf] block only.

Link AI assistant messages to the specific assistant. postReply now stamps
the assistant id into the message meta, and the message bubble links to that
assistant's edit page, falling back to the assistants list for older messages
that lack the id.
2026-07-21 11:02:43 +05:30
Abhinav Raut c659e30eca address CodeRabbit review findings in the AI agent
- recover from panics in the AI reply and FAQ-mining workers so one bad run can't crash the process
- guard image decode with a pixel-count cap to block image bombs
- hand off (not silently drop) when the confirmation reply fails to send
- give the model a generic tool-failure message instead of the raw error
- log Redis Expire and assistant-cache refresh failures instead of ignoring them
- drop chunk text from RAG debug logs
- refetch the assistant when the edit route's id changes
2026-07-21 01:05:26 +05:30
dependabot[bot] b4228332ed build(deps): bump axios from 1.16.0 to 1.18.0 in /frontend
Bumps [axios](https://github.com/axios/axios) from 1.16.0 to 1.18.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.16.0...v1.18.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.18.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-20 18:11:24 +00:00
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 6015859574 scope copilot thinking state per conversation
isThinking was a single global ref, so switching conversations while a copilot
send was still in flight showed the thinking indicator in the wrong conversation
and blocked sending there until the other request settled. Key it by uuid like
the revision and message state already are.
2026-07-19 23:57:01 +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 dd3465f6bc make the quoted text toggle keyboard accessible 2026-07-19 18:58:36 +05:30
Abhinav Raut b570502a20 remember when a provider needs max_completion_tokens and track token usage
Some OpenAI-compatible endpoints reject max_tokens and want
max_completion_tokens. Before, every request paid a 400 round trip to
learn this. Now we cache the swap per base URL and model, so once one
request adapts, later requests send the right param up front. Only adapt
when the error code is unsupported_parameter, not on any max_tokens error.

Parse the usage block from provider responses into a TokenUsage struct and
log prompt/completion/total tokens. Added debug logging of model content,
tool results, and RAG chunk text to make agent runs easier to trace.

Widget side: clean up chat bubble spacing by moving margins off the text
and onto trailing elements, and drop the bottom margin on the last
paragraph/list in rendered HTML so bubbles don't have extra padding.
2026-07-19 18:47:36 +05:30
Abhinav Raut ec5333a393 move OIDC client secret masking to the backend
The frontend used to blank out the masked secret before saving. Now it sends the value as-is and the backend keeps the stored secret when it sees the dummy mask. This matches how webhooks and other secrets already work.
2026-07-19 12:35:07 +05:30
Abhinav Raut 35d69fc1c3 fix review findings in reports overview and avatar upload
Guard avatar edits while a save is in flight. The AvatarUpload now takes
a disabled prop that blocks the file picker and the remove button, and
the profile view ignores crop/remove events while saving. This stops a
second crop or a remove from racing an ongoing update.

Only clear the pending file after the delete request succeeds, so a
failed remove keeps the cropped preview and the Save button usable.

Do not mark failed report refreshes as fresh. The fetch helpers now
report success, and lastUpdate only advances when every section loads.
A fully failed refresh no longer labels stale data as just updated.

Add an aria-label to the custom range days input so assistive tech can
name it.
2026-07-18 19:55:59 +05:30
Abhinav Raut 94334a02c6 polish reports overview, avatar upload, and conversation list UI
Reports overview now has a single range picker (7/30/90 days plus a
custom input) that drives every card at once, instead of a separate
date dropdown per card. Auto-refresh polls every 5 minutes, pauses
when the tab is hidden, and stops after an hour until you refresh.
Card titles are muted, numbers use tabular figures, and SLA colors use
theme tokens instead of raw green/red. The old DateFilter component is
gone.

Move avatar cropping into the shared AvatarUpload component and reuse
it on the profile page. Save is disabled until a new image is picked.

Conversation list emphasizes unread rows with bolder text and tighter
spacing. Empty state, MenuCard, and chart legend get small style
fixes. Consolidate several one-off i18n strings into shared keys.
2026-07-18 19:42:42 +05:30
Abhinav Raut 8ac465c51a support multiple auth headers on AI tools and tune agent timeouts
Tool auth used to be a single header/value pair. It is now a list of
headers, each value encrypted at rest and masked in the API. Editing a
tool keeps an untouched (masked) value's existing secret, matched by
header name. Covered with unit tests.

Agent tuning: a full agent run is now capped (90s for livechat since the
customer is watching, 3min for email), the per-provider-call timeout
drops 90s to 60s with 2 retries, and default worker_count goes 2 to 10
for burst headroom. Idle workers are free; the run cap is what protects
the pool.

Also adds help text and placeholders across the AI admin pages.
2026-07-18 18:11:10 +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 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