Skip to content

Feat/ai agent - #421

Merged
abhinavxd merged 86 commits into
mainfrom
feat/ai-agent
Jul 29, 2026
Merged

Feat/ai agent#421
abhinavxd merged 86 commits into
mainfrom
feat/ai-agent

Conversation

@abhinavxd

@abhinavxd abhinavxd commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added an admin AI console for providers (by type), tools, knowledge-base snippets (including URL import), and AI assistants (CRUD, preview, stats, avatar).
    • Added AI conversation capabilities: Copilot chat with persona, “Generate reply”, “Summarize with AI”, suggested tags, and an AI FAQ learning approval/rejection queue.
  • Improvements / Bug Fixes
    • Improved reply-generation coordination to prevent duplicate runs and only apply results to the active conversation; added richer Copilot history management.
    • Ensured “Resolved” conversations reliably mark assignment notifications as read.
  • Security
    • Centralized SSRF protection for outbound admin AI/tool requests and restricted sign-in to agent accounts.

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.
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.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds provider-scoped AI configuration, embeddings, knowledge-base search, custom tools, autonomous assistants, FAQ mining, Copilot chat, and administration interfaces. It also adds migrations, conversation integration, SSRF-controlled outbound clients, avatar cropping, reporting refresh changes, and related UI updates.

Changes

AI platform and assistant operations

Layer / File(s) Summary
Provider, retrieval, and agent execution
internal/ai/*, internal/aiagent/*, internal/stringutil/*, schema.sql, internal/migrations/*
Adds provider routing, OpenAI-compatible chat and embedding clients, token-aware HTML chunking, knowledge-base indexing, custom HTTP tools, FAQ mining, assistant execution, and AI persistence schema.
HTTP APIs and administration
cmd/ai*.go, cmd/handlers.go, frontend/apps/main/src/api/*, frontend/apps/main/src/views/admin/ai/*, frontend/apps/main/src/features/admin/ai/*
Adds AI configuration, tools, snippets, assistants, FAQ, preview, statistics, Copilot, and conversation-generation endpoints with corresponding admin forms and views.
Conversation and Copilot integration
internal/conversation/*, frontend/apps/main/src/features/conversation/*, frontend/apps/main/src/stores/copilot.js
Connects AI assistants to assignment and resolution events, adds Copilot history and persona selection, supports reply generation, summarization, tag suggestions, and assistant message actions.
Runtime, security, and supporting UI
cmd/init.go, cmd/main.go, internal/ssrf/*, internal/auth/*, internal/webhook/*, frontend/*, config.sample.toml
Wires AI workers and shared SSRF controls, updates assistant identity handling, adds widget and tool test utilities, and applies related theme, reporting, avatar, and accessibility changes.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the change but too generic to clearly describe the main feature work. Use a specific title that names the primary change, such as adding AI agent and copilot features.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ai-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

abhinavxd added 18 commits July 12, 2026 04:47
The agent now only replies to a turn the primary contact authored, so a CC'd or plus-address participant hands off to a human instead of driving tool calls under the contact's identity. It also asks the customer to confirm before resolving, and can read the contact's other recent conversations for context.
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.
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.
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.
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.
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.
Fetch agents through the API in AgentList instead of the shared users store, so the list does not depend on store state populated elsewhere.
A single-file page to test the livechat widget's JWT contact auth locally. Signs an HS256 token in the browser from an editable payload and loads the widget with it.
Stop dropping customer follow-ups sent mid-response, reset the turn cap only on reassignment, send max_tokens for non-reasoning models, and make FAQ approval atomic. Also exclude CSAT surveys from assistant stats and remove unused scoped-search code.
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.
…ff, 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
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.
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.
…rrors

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.
@abhinavxd
abhinavxd marked this pull request as ready for review July 13, 2026 19:21

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🧹 Nitpick comments (4)
internal/ai/queries.sql (1)

43-44: 🚀 Performance & Scalability | 🔵 Trivial

get-all-embeddings loads the entire embeddings table on every search.

With no vector index (embeddings stored as BYTEA), fetching all rows for in-memory similarity works at small scale but degrades linearly as the knowledge base grows (memory, deserialization, and IO per query). If snippet volume is expected to grow, consider pgvector with an ANN index (ivfflat/hnsw) or at least filtering by source_type and paginating.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/ai/queries.sql` around lines 43 - 44, Update the get-all-embeddings
query and its callers to avoid loading the entire embeddings table for every
search: prefer pgvector similarity search with an appropriate ANN index, or
minimally add source_type filtering and pagination while preserving search
results. Ensure the related embedding retrieval method passes the filter and
page parameters instead of unconditionally fetching all rows.
internal/conversation/message.go (1)

704-716: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider logging the json.Marshal error for diagnostics.

meta, _ := json.Marshal(...) suppresses the error at line 705. While json.Marshal on map[string]string cannot fail in practice (Go strings are never nil), logging the error would aid debugging if the input ever changes to a richer type in the future.

♻️ Optional: log the marshal error
-	meta, _ := json.Marshal(map[string]string{"activity_type": activityType})
+	meta, err := json.Marshal(map[string]string{"activity_type": activityType})
+	if err != nil {
+		m.lo.Error("error marshaling activity meta", "activity_type", activityType, "error", err)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/conversation/message.go` around lines 704 - 716, Handle the error
returned by json.Marshal in the activity message construction before assigning
Meta, and log it through the existing conversation/message logging mechanism
while preserving the current metadata behavior. Update the code around the
activity message creation rather than changing the Message fields or
activity_type payload.
internal/stringutil/htmlchunker.go (1)

273-287: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Optional: replace rune-by-rune truncation with binary search.

The loop strips one rune at a time and re-tokenizes on each step, giving O(n²) behavior for oversized blocks. Since TokenizerFunc is monotonic in length, a binary search over the rune length reaches the fit point in O(log n) tokenizer calls.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/stringutil/htmlchunker.go` around lines 273 - 287, Optimize the
truncation loop in the boundary-processing logic by replacing rune-by-rune
removal with a binary search over the rune slice length. Use the monotonic
TokenizerFunc result to find the longest prefix whose token count is at most
cfg.MaxTokens, then preserve the existing warning, boundary.Content,
boundary.Tokens, and return behavior.
internal/ai/ai.go (1)

240-240: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer ExecContext over Exec for the prepared statements. golangci-lint (noctx) flags Lines 240 and 298. Threading a context.Context through UpdateProviderConfig/UpdateProvider and using ExecContext enables cancellation and honors request deadlines on these writes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/ai/ai.go` at line 240, Update the call sites in the relevant AI
manager methods around UpdateProviderConfig and UpdateProvider to accept and
propagate a context.Context, then replace the prepared-statement Exec calls with
ExecContext using that context. Preserve the existing write behavior while
ensuring request cancellation and deadlines reach both updates.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/aiagent.go`:
- Around line 48-56: Make the assistant/avatar mutation flows failure-safe in
cmd/aiagent.go: in the CreateAssistant flow around lines 48-56, replace sole
best-effort deletion rollback with coordinated cleanup that prevents partial
state; in the avatar-application flow around lines 78-83, restore the
assistant’s prior fields when applyAssistantAvatar fails; and around lines
171-179, update deletion ordering so media is not removed while database records
still reference it. Use the existing assistant, avatar, and media mutation
symbols and preserve error responses.

In `@frontend/apps/main/src/features/conversation/ReplyBox.vue`:
- Around line 306-314: Update handleGenerateReply to capture the current
conversation UUID before awaiting api.aiGenerateReply, then compare it with
currentConversationUUID.value before assigning htmlContent.value. Only apply the
generated response when the UUID still matches; preserve the existing request
and generation-state behavior.

In `@frontend/apps/main/src/features/conversation/sidebar/CopilotPanel.vue`:
- Line 4: Update the CopilotPanel.vue conversation operations around hydrate(),
clearChat, and aiCopilot() to maintain a per-UUID operation revision. Capture
the current revision before each asynchronous request, increment it when
clearing or otherwise starting a newer operation, and ignore any response whose
captured revision no longer matches before updating the store, preventing stale
hydration or assistant messages from restoring cleared history.

In `@internal/ai/agent.go`:
- Around line 58-98: Thread the caller context through the agent’s
chat-completion path: update chatCompletion and its underlying
SendChatCompletion/doRequest calls to accept and propagate ctx, including retry
or backoff waits. Update both completion invocations in the agent loop so
cancellation and deadlines stop in-flight requests promptly.

In `@internal/ai/embedding.go`:
- Around line 107-114: Update the debug logging around the RAG search loop in
the embedding method to remove raw query text and chunk previews. Retain safe
metadata such as query length, result count, ranks, scores, source types, and
source IDs, ensuring no customer or knowledge-base content is logged.
- Around line 146-151: Update the deferred rollback in the reindex transaction
flow around BeginTxx to handle or explicitly discard the Rollback result so it
satisfies errcheck, while preserving the existing transaction error propagation
and cleanup behavior.

In `@internal/ai/knowledgebase.go`:
- Around line 61-69: Update DeleteKnowledgeBaseItem to execute both the snippet
deletion and embedding cleanup within one database transaction, rolling back and
returning an error if either operation fails. Only invoke the in-memory index
removal after the transaction commits successfully, rather than calling
RemoveEmbeddings independently and ignoring its failure.
- Around line 72-100: Update the reindexing flow around reindexSnippet and
reindexSnippetWith so concurrent updates for the same snippet cannot commit
out-of-date vectors or fingerprints. Replace the independent per-update
goroutine behavior with synchronous indexing or a serialized/versioned queue
keyed by snippet ID, ensuring an older request cannot overwrite a newer edit
while preserving the existing enabled/disabled handling.
- Around line 116-119: Handle the error returned by fmt.Fprintf in
snippetFingerprint instead of ignoring it, while preserving the existing hash
input and fingerprint output behavior.

In `@internal/ai/openai.go`:
- Line 264: Update the response-body read in the OpenAI request flow to use the
bounded-reader pattern from internal/ai/tools.go, enforcing the provider
response size limit. Stop ignoring the read error: propagate failures from
reading resp.Body, including oversized or truncated responses, through the
existing error-handling path.
- Around line 175-186: Update the embedding loop in the parsed.Data handling to
track indices already seen and reject any duplicate before assigning to
out[d.Index]. Preserve the existing range validation and return an error
identifying the repeated index, ensuring every output slot is populated
uniquely.

In `@internal/ai/provider.go`:
- Around line 6-10: Update internal/ai/provider.go:6-10 so every ProviderClient
method accepts a context.Context, then update internal/ai/openai.go:210-250 and
the corresponding OpenAIClient implementations to propagate that context into
HTTP requests and make retry backoff cancellation-aware, returning promptly when
the context is cancelled; update all affected callers to pass their context
through.

In `@internal/aiagent/faq.go`:
- Around line 83-96: Make FAQ approval atomic and recoverable in the approval
flow around ApproveFAQSuggestionIfPending and CreateKnowledgeBaseItem: ensure
snippet creation and the pending-to-approved transition cannot leave
inconsistent state, using a transaction or an explicitly recoverable
intermediate state. In internal/aiagent/faq.go lines 100-106, update rejection
to conditionally transition only pending suggestions to rejected and return the
existing conflict error when no rows are affected.

In `@internal/aiagent/tools.go`:
- Line 82: Update the debug log in the knowledge-search flow to remove the raw
in.Query field, while preserving non-content metadata such as hit count, top
score, and minimum confidence.

In `@internal/stringutil/htmlchunker.go`:
- Around line 130-136: Explicitly discard the html.Render error at both affected
call sites: internal/stringutil/htmlchunker.go lines 130-136 in the extract
closure, and lines 204-206 where rendering uses buf and c. Update each call to
assign the returned error to the blank identifier without changing surrounding
behavior.

In `@scripts/widget-jwt-test/index.html`:
- Around line 92-100: Update loadWidgetScript and the surrounding widget test
flow so the signing secret is never exposed to user-controlled widget.js or
persisted in localStorage: keep signing in the parent context, then load the
widget inside an isolated sandboxed iframe and pass only the resulting JWT to
it. Remove secret persistence and direct same-document script injection, and
preserve the existing widget initialization behavior through the JWT-only frame
communication.

In `@scripts/widget-jwt-test/README.md`:
- Around line 11-13: Update the Markdown command fence in the widget JWT test
README to include the shell language identifier, changing the untyped fence
around the http.server command to a shell-labeled fence so it satisfies
markdownlint MD040.

---

Nitpick comments:
In `@internal/ai/ai.go`:
- Line 240: Update the call sites in the relevant AI manager methods around
UpdateProviderConfig and UpdateProvider to accept and propagate a
context.Context, then replace the prepared-statement Exec calls with ExecContext
using that context. Preserve the existing write behavior while ensuring request
cancellation and deadlines reach both updates.

In `@internal/ai/queries.sql`:
- Around line 43-44: Update the get-all-embeddings query and its callers to
avoid loading the entire embeddings table for every search: prefer pgvector
similarity search with an appropriate ANN index, or minimally add source_type
filtering and pagination while preserving search results. Ensure the related
embedding retrieval method passes the filter and page parameters instead of
unconditionally fetching all rows.

In `@internal/conversation/message.go`:
- Around line 704-716: Handle the error returned by json.Marshal in the activity
message construction before assigning Meta, and log it through the existing
conversation/message logging mechanism while preserving the current metadata
behavior. Update the code around the activity message creation rather than
changing the Message fields or activity_type payload.

In `@internal/stringutil/htmlchunker.go`:
- Around line 273-287: Optimize the truncation loop in the boundary-processing
logic by replacing rune-by-rune removal with a binary search over the rune slice
length. Use the monotonic TokenizerFunc result to find the longest prefix whose
token count is at most cfg.MaxTokens, then preserve the existing warning,
boundary.Content, boundary.Tokens, and return behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f619979-265e-466b-8365-11fd95839bbc

📥 Commits

Reviewing files that changed from the base of the PR and between 2fe850a and 976c45d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (86)
  • cmd/ai.go
  • cmd/aiagent.go
  • cmd/conversation.go
  • cmd/handlers.go
  • cmd/init.go
  • cmd/main.go
  • cmd/upgrade.go
  • cmd/users.go
  • config.sample.toml
  • frontend/apps/main/src/api/index.js
  • frontend/apps/main/src/components/sidebar/Sidebar.vue
  • frontend/apps/main/src/constants/navigation.js
  • frontend/apps/main/src/features/admin/ai/AssistantForm.vue
  • frontend/apps/main/src/features/admin/ai/ProviderForm.vue
  • frontend/apps/main/src/features/admin/ai/SnippetForm.vue
  • frontend/apps/main/src/features/admin/ai/ToolForm.vue
  • frontend/apps/main/src/features/admin/ai/assistantColumns.js
  • frontend/apps/main/src/features/admin/ai/assistantDropdown.vue
  • frontend/apps/main/src/features/admin/ai/snippetColumns.js
  • frontend/apps/main/src/features/admin/ai/snippetDropdown.vue
  • frontend/apps/main/src/features/admin/ai/suggestionColumns.js
  • frontend/apps/main/src/features/admin/ai/toolColumns.js
  • frontend/apps/main/src/features/admin/ai/toolDropdown.vue
  • frontend/apps/main/src/features/admin/general/GeneralSettingForm.vue
  • frontend/apps/main/src/features/admin/general/formSchema.js
  • frontend/apps/main/src/features/admin/sla/SLAForm.vue
  • frontend/apps/main/src/features/conversation/ReplyBox.vue
  • frontend/apps/main/src/features/conversation/ReplyBoxContent.vue
  • frontend/apps/main/src/features/conversation/ReplyBoxMenuBar.vue
  • frontend/apps/main/src/features/conversation/message/MessageBubble.vue
  • frontend/apps/main/src/features/conversation/sidebar/ConversationSideBar.vue
  • frontend/apps/main/src/features/conversation/sidebar/CopilotPanel.vue
  • frontend/apps/main/src/layouts/admin/AdminLayout.vue
  • frontend/apps/main/src/router/index.js
  • frontend/apps/main/src/stores/appSettings.js
  • frontend/apps/main/src/stores/conversation.js
  • frontend/apps/main/src/stores/users.js
  • frontend/apps/main/src/views/admin/agents/AgentList.vue
  • frontend/apps/main/src/views/admin/ai/AIAssistants.vue
  • frontend/apps/main/src/views/admin/ai/AIProviders.vue
  • frontend/apps/main/src/views/admin/ai/AISnippets.vue
  • frontend/apps/main/src/views/admin/ai/AISuggestions.vue
  • frontend/apps/main/src/views/admin/ai/AITools.vue
  • frontend/apps/main/src/views/admin/ai/CreateOrEditAssistant.vue
  • frontend/apps/main/src/views/admin/ai/CreateOrEditTool.vue
  • frontend/apps/widget/src/components/ChatTitle.vue
  • frontend/shared-ui/components/ui/button/Button.vue
  • go.mod
  • i18n/en-US.json
  • internal/ai/agent.go
  • internal/ai/ai.go
  • internal/ai/copilot.go
  • internal/ai/embedding.go
  • internal/ai/knowledgebase.go
  • internal/ai/models/models.go
  • internal/ai/openai.go
  • internal/ai/provider.go
  • internal/ai/queries.sql
  • internal/ai/tools.go
  • internal/ai/toolstore.go
  • internal/aiagent/aiagent.go
  • internal/aiagent/faq.go
  • internal/aiagent/models/models.go
  • internal/aiagent/prompt.go
  • internal/aiagent/queries.sql
  • internal/aiagent/tools.go
  • internal/aiagent/worker.go
  • internal/conversation/continuity.go
  • internal/conversation/conversation.go
  • internal/conversation/message.go
  • internal/conversation/models/models.go
  • internal/conversation/queries.sql
  • internal/image/image.go
  • internal/migrations/v2.7.0.go
  • internal/setting/models/models.go
  • internal/stringutil/htmlchunker.go
  • internal/stringutil/htmlchunker_test.go
  • internal/stringutil/stringutil.go
  • internal/user/agent.go
  • internal/user/models/models.go
  • internal/user/queries.sql
  • schema.sql
  • scripts/tools-server/README.md
  • scripts/tools-server/main.go
  • scripts/widget-jwt-test/README.md
  • scripts/widget-jwt-test/index.html
💤 Files with no reviewable changes (1)
  • cmd/conversation.go

Comment thread cmd/aiagent.go
Comment thread frontend/apps/main/src/features/conversation/ReplyBox.vue Outdated
Comment thread frontend/apps/main/src/features/conversation/sidebar/CopilotPanel.vue Outdated
Comment thread internal/ai/agent.go
Comment thread internal/ai/embedding.go Outdated
Comment thread internal/aiagent/faq.go
Comment thread internal/aiagent/tools.go Outdated
Comment thread internal/stringutil/htmlchunker.go Outdated
Comment thread scripts/widget-jwt-test/index.html
Comment thread scripts/widget-jwt-test/README.md Outdated
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.
…y 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/ai/urlimport.go`:
- Around line 27-32: Update the URL-fetch flow around url.Parse and m.fetchURL
to use a dedicated HTTP client with a transport that resolves and rejects
loopback, link-local, RFC1918, and other private or reserved destination IPs at
connection time, disables untrusted proxy use, and applies the validation to
every redirect. Preserve the existing HTTP/HTTPS and host checks while ensuring
fetchURL uses this client for the complete request chain.
- Around line 32-35: Sanitize the URL logged in the fetchURL error path before
passing it to m.lo.Error. Update the error context in the URL import flow to
include only the host and path, or an equivalent representation with query
parameters and credentials removed, while preserving the existing fetch failure
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9712b5dd-fda7-40e6-95c0-73a82c1d8e69

📥 Commits

Reviewing files that changed from the base of the PR and between afb338c and 8cdfe86.

📒 Files selected for processing (35)
  • cmd/ai.go
  • cmd/aiagent.go
  • cmd/handlers.go
  • frontend/apps/main/src/App.vue
  • frontend/apps/main/src/api/index.js
  • frontend/apps/main/src/components/button/CopyButton.vue
  • frontend/apps/main/src/components/importer/Importer.vue
  • frontend/apps/main/src/components/layout/MenuCard.vue
  • frontend/apps/main/src/components/sidebar/Sidebar.vue
  • frontend/apps/main/src/features/admin/agents/AgentForm.vue
  • frontend/apps/main/src/features/admin/ai/AssistantForm.vue
  • frontend/apps/main/src/features/admin/ai/ProviderForm.vue
  • frontend/apps/main/src/features/admin/ai/snippetColumns.js
  • frontend/apps/main/src/features/admin/business-hours/BusinessHoursForm.vue
  • frontend/apps/main/src/features/conversation/Conversation.vue
  • frontend/apps/main/src/features/conversation/list/ConversationEmptyList.vue
  • frontend/apps/main/src/views/admin/ai/AISnippets.vue
  • frontend/apps/main/src/views/admin/ai/CreateOrEditAssistant.vue
  • frontend/apps/main/src/views/reports/OverviewView.vue
  • i18n/en-US.json
  • internal/ai/copilot.go
  • internal/ai/knowledgebase.go
  • internal/ai/models/models.go
  • internal/ai/queries.sql
  • internal/ai/urlimport.go
  • internal/aiagent/aiagent.go
  • internal/aiagent/faq.go
  • internal/aiagent/models/models.go
  • internal/aiagent/prompt.go
  • internal/aiagent/queries.sql
  • internal/aiagent/tools.go
  • internal/aiagent/worker.go
  • internal/migrations/v2.7.0.go
  • internal/stringutil/htmlextract.go
  • schema.sql
🚧 Files skipped from review as they are similar to previous changes (18)
  • frontend/apps/main/src/features/admin/ai/snippetColumns.js
  • internal/aiagent/prompt.go
  • cmd/handlers.go
  • internal/ai/copilot.go
  • frontend/apps/main/src/features/admin/ai/ProviderForm.vue
  • frontend/apps/main/src/components/sidebar/Sidebar.vue
  • internal/ai/queries.sql
  • internal/aiagent/tools.go
  • frontend/apps/main/src/features/admin/ai/AssistantForm.vue
  • internal/ai/knowledgebase.go
  • cmd/aiagent.go
  • internal/aiagent/faq.go
  • internal/aiagent/queries.sql
  • frontend/apps/main/src/api/index.js
  • internal/aiagent/aiagent.go
  • internal/ai/models/models.go
  • schema.sql
  • internal/migrations/v2.7.0.go

Comment thread internal/ai/urlimport.go
Comment thread internal/ai/urlimport.go
…tch 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/ai/tokenizer.go`:
- Around line 53-65: Update capToTokens to return an empty string immediately
when maxTokens is zero or negative, before either the encoder or fallback branch
performs length checks or slicing; preserve the existing behavior for positive
limits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fbf01fea-f583-4849-9fc7-f712427bd4c1

📥 Commits

Reviewing files that changed from the base of the PR and between 8cdfe86 and c030c9e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (17)
  • frontend/apps/main/src/App.vue
  • frontend/apps/main/src/features/admin/ai/SnippetForm.vue
  • frontend/apps/main/src/features/conversation/Conversation.vue
  • frontend/apps/main/src/views/admin/ai/AISnippets.vue
  • frontend/shared-ui/assets/styles/main.scss
  • frontend/shared-ui/components/ScrollToBottomButton/ScrollToBottomButton.vue
  • go.mod
  • i18n/en-US.json
  • internal/ai/ai.go
  • internal/ai/embedding.go
  • internal/ai/models/models.go
  • internal/ai/openai.go
  • internal/ai/openai_test.go
  • internal/ai/tokenizer.go
  • internal/ai/tokenizer_test.go
  • internal/aiagent/prompt.go
  • internal/aiagent/worker.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • frontend/apps/main/src/features/conversation/Conversation.vue
  • frontend/apps/main/src/features/admin/ai/SnippetForm.vue
  • frontend/apps/main/src/views/admin/ai/AISnippets.vue
  • i18n/en-US.json
  • internal/ai/embedding.go
  • internal/ai/models/models.go
  • internal/aiagent/worker.go
  • internal/ai/ai.go
  • internal/ai/openai.go

Comment thread internal/ai/tokenizer.go
Fix a reindex race: snippet embedding runs outside the lock, so a slower
job from an older edit could commit stale vectors after a newer edit.
Split Reindex into embed (lock-free) and commit (locked), and gate the
commit with a per-snippet generation counter so only the latest edit
wins. Delete drops the counter so an in-flight job can't re-insert
vectors for a deleted snippet.

Clear the DB avatar reference before deleting the avatar media file. The
old order deleted the file first, so a failed DB update left the DB
pointing at a missing file and a broken image.

Also handle unchecked errors flagged by errcheck (tx.Rollback,
html.Render, fmt.Fprintf), guard capToTokens against a negative limit
that would panic, and stop logging the full import URL since it can
carry credentials.
Move the webhook SSRF guard into a shared internal/ssrf package and wire it
into every place the server fetches an admin-set URL: webhooks, OIDC discovery,
the AI provider base URL, and custom AI tool calls. Add a global [ssrf] config
block, off by default with an allowed_cidrs bypass, so single-tenant self-hosters
keep reaching internal hosts while multi-tenant or hosted deploys can turn it on.
The old [webhook] allowed_hosts key is still read for backward compat and folds
into the guard.
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.
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.
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.
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/ai/embedding.go (1)

279-297: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Reconcile swallows provider/config errors silently.

getRawProviderConfig and GetKnowledgeBaseItems failures both just return with no logging. Since this runs on every reconcile tick, a DB blip, decryption failure, or missing provider row will silently stop the entire embedding reconciliation loop indefinitely with zero operator signal — the knowledge base can go stale for RAG search without any trace in the logs.

🛠️ Proposed fix
 	cfg, err := m.getRawProviderConfig(models.ProviderTypeEmbedding)
 	if err != nil {
+		m.lo.Error("error fetching embedding provider config for reconcile", "error", err)
 		return
 	}
 	// The API key is stored encrypted, so a non-empty value is enough to know a provider is configured.
 	if cfg.APIKey == "" {
 		return
 	}
 	items, err := m.GetKnowledgeBaseItems()
 	if err != nil {
+		m.lo.Error("error fetching knowledge base items for reconcile", "error", err)
 		return
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/ai/embedding.go` around lines 279 - 297, Update reconcile to log the
errors returned by getRawProviderConfig and GetKnowledgeBaseItems before
returning, including enough context to identify whether provider configuration
loading or knowledge-base item retrieval failed. Preserve the existing
early-return behavior after logging.
internal/aiagent/worker.go (1)

340-345: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fail closed when the freshness check errors.

A failed GetConversation falls through to reply and resolve using stale assignment/status data, so the agent can talk over a human takeover during a database failure. Log and return (or retry) before mutating the conversation.

Proposed fix
- if fresh, ferr := m.convo.GetConversation(convID, "", ""); ferr == nil {
-   if !fresh.AssignedUserID.Valid || int(fresh.AssignedUserID.Int) != assistant.UserID || nonActionableCategories[fresh.StatusCategory.String] {
-     m.lo.Debug("ai agent conversation changed mid-run, dropping reply", "conversation_uuid", conv.UUID)
-     return
-   }
+ fresh, ferr := m.convo.GetConversation(convID, "", "")
+ if ferr != nil {
+   m.lo.Error("error rechecking conversation before ai reply", "conversation_uuid", conv.UUID, "error", ferr)
+   return
+ }
+ if !fresh.AssignedUserID.Valid || int(fresh.AssignedUserID.Int) != assistant.UserID || nonActionableCategories[fresh.StatusCategory.String] {
+   m.lo.Debug("ai agent conversation changed mid-run, dropping reply", "conversation_uuid", conv.UUID)
+   return
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/aiagent/worker.go` around lines 340 - 345, Update the freshness
check around m.convo.GetConversation in the worker flow to fail closed when ferr
is non-nil: log the lookup error and return before sending a reply or resolving
the conversation. Preserve the existing mismatch checks for successful lookups,
including the assigned-user and non-actionable-status validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/apps/main/src/features/admin/inbox/EmailInboxForm.vue`:
- Line 1043: Replace the direct setValues calls in the form watchers with the
supported hydration API that updates initial values without validation and
resets dirty/touched state. Apply this change at
frontend/apps/main/src/features/admin/inbox/EmailInboxForm.vue:1043,
frontend/apps/main/src/features/admin/inbox/LivechatInboxForm.vue:1431, and
frontend/apps/main/src/features/admin/macros/MacroForm.vue:228, preserving each
watcher’s existing rewritten values.

In `@frontend/apps/main/src/features/view/ViewForm.vue`:
- Line 198: Update the view-loading logic around form.setValues to reset the
form state when replacing the current record. Use form.resetForm with
processedVal as the new values, or explicitly clear validation errors and
touched state before loading, while preserving the processed values.

In `@frontend/apps/main/src/views/auth/SetPasswordView.vue`:
- Around line 172-181: Align validateForm(), passwordHasError, and
confirmPasswordHasError in SetPasswordView so they use identical password
requirements: reject empty or fewer-than-8-character passwords and reject
mismatched confirmation values. Ensure the computed error states also mark empty
invalid fields, including the confirmation field, so destructive styling matches
validation and prevents api.setPassword for any invalid password.

In `@frontend/DESIGN.md`:
- Around line 82-86: Add the text language identifier to the opening fenced code
block in DESIGN.md, preserving the existing diagram content and closing fence.

In `@internal/aiagent/worker.go`:
- Around line 677-683: Update the debug logging in the trimmed-message handling
block to stop emitting the removed customer content. Keep the existing removal
calculation if needed, but replace the “removed” log value with its length,
using the surrounding `trimmed`, `full`, and `m.lo.Debug` flow.

In `@internal/stringutil/emailquote.go`:
- Around line 80-100: Update the class checks in isQuoteContainerNode and
isQuoteMarkerNode to split the class attribute into whitespace-delimited tokens
and compare each token exactly against quoteContainerClasses or
quoteMarkerClasses. Remove the substring-based strings.Contains matching while
preserving the existing ID check and boolean behavior.

---

Outside diff comments:
In `@internal/ai/embedding.go`:
- Around line 279-297: Update reconcile to log the errors returned by
getRawProviderConfig and GetKnowledgeBaseItems before returning, including
enough context to identify whether provider configuration loading or
knowledge-base item retrieval failed. Preserve the existing early-return
behavior after logging.

In `@internal/aiagent/worker.go`:
- Around line 340-345: Update the freshness check around m.convo.GetConversation
in the worker flow to fail closed when ferr is non-nil: log the lookup error and
return before sending a reply or resolving the conversation. Preserve the
existing mismatch checks for successful lookups, including the assigned-user and
non-actionable-status validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 485076b5-e502-44ae-a354-ba7c1ed37be1

📥 Commits

Reviewing files that changed from the base of the PR and between aa200f5 and 6b8a0f9.

📒 Files selected for processing (168)
  • cmd/aitools.go
  • cmd/conversation.go
  • cmd/handlers.go
  • cmd/init.go
  • cmd/main.go
  • frontend/DESIGN.md
  • frontend/apps/main/src/components/DownloadLink.vue
  • frontend/apps/main/src/components/KeyboardShortcutsDialog.vue
  • frontend/apps/main/src/components/banner/AdminBanner.vue
  • frontend/apps/main/src/components/button/CloseButton.vue
  • frontend/apps/main/src/components/button/CopyButton.vue
  • frontend/apps/main/src/components/datatable/DataTable.vue
  • frontend/apps/main/src/components/editor/TextEditor.vue
  • frontend/apps/main/src/components/sidebar/NotificationBell.vue
  • frontend/apps/main/src/components/sidebar/Sidebar.vue
  • frontend/apps/main/src/components/sidebar/SidebarNavUser.vue
  • frontend/apps/main/src/components/table/SimpleTable.vue
  • frontend/apps/main/src/constants/navigation.js
  • frontend/apps/main/src/features/admin/agents/AgentForm.vue
  • frontend/apps/main/src/features/admin/agents/dataTableColumns.js
  • frontend/apps/main/src/features/admin/agents/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/ai/AssistantForm.vue
  • frontend/apps/main/src/features/admin/ai/ProviderForm.vue
  • frontend/apps/main/src/features/admin/ai/SnippetForm.vue
  • frontend/apps/main/src/features/admin/ai/ToolForm.vue
  • frontend/apps/main/src/features/admin/ai/assistantColumns.js
  • frontend/apps/main/src/features/admin/ai/assistantDropdown.vue
  • frontend/apps/main/src/features/admin/ai/snippetColumns.js
  • frontend/apps/main/src/features/admin/ai/snippetDropdown.vue
  • frontend/apps/main/src/features/admin/ai/suggestionColumns.js
  • frontend/apps/main/src/features/admin/ai/toolColumns.js
  • frontend/apps/main/src/features/admin/ai/toolDropdown.vue
  • frontend/apps/main/src/features/admin/automation/ActionBox.vue
  • frontend/apps/main/src/features/admin/automation/RuleBox.vue
  • frontend/apps/main/src/features/admin/automation/RuleList.vue
  • frontend/apps/main/src/features/admin/automation/RuleTab.vue
  • frontend/apps/main/src/features/admin/business-hours/BusinessHoursForm.vue
  • frontend/apps/main/src/features/admin/business-hours/dataTableColumns.js
  • frontend/apps/main/src/features/admin/business-hours/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/context-links/dataTableColumns.js
  • frontend/apps/main/src/features/admin/context-links/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/custom-attributes/dataTableColumns.js
  • frontend/apps/main/src/features/admin/custom-attributes/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/general/GeneralSettingForm.vue
  • frontend/apps/main/src/features/admin/inbox/EmailInboxForm.vue
  • frontend/apps/main/src/features/admin/inbox/InboxDataTableDropDown.vue
  • frontend/apps/main/src/features/admin/inbox/LivechatInboxForm.vue
  • frontend/apps/main/src/features/admin/inbox/LivechatWidgetPreview.vue
  • frontend/apps/main/src/features/admin/inbox/PreChatFormConfig.vue
  • frontend/apps/main/src/features/admin/macros/ActionBuilder.vue
  • frontend/apps/main/src/features/admin/macros/MacroForm.vue
  • frontend/apps/main/src/features/admin/macros/dataTableColumns.js
  • frontend/apps/main/src/features/admin/macros/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/notification/NotificationSettingForm.vue
  • frontend/apps/main/src/features/admin/oidc/OIDCForm.vue
  • frontend/apps/main/src/features/admin/oidc/dataTableColumns.js
  • frontend/apps/main/src/features/admin/oidc/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/oidc/formSchema.js
  • frontend/apps/main/src/features/admin/roles/RoleForm.vue
  • frontend/apps/main/src/features/admin/roles/dataTableColumns.js
  • frontend/apps/main/src/features/admin/roles/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/shared-views/SharedViewForm.vue
  • frontend/apps/main/src/features/admin/shared-views/dataTableColumns.js
  • frontend/apps/main/src/features/admin/shared-views/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/sla/SLAForm.vue
  • frontend/apps/main/src/features/admin/sla/dataTableColumns.js
  • frontend/apps/main/src/features/admin/sla/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/status/dataTableColumns.js
  • frontend/apps/main/src/features/admin/status/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/tags/dataTableColumns.js
  • frontend/apps/main/src/features/admin/tags/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/teams/TeamDataTableDropdown.vue
  • frontend/apps/main/src/features/admin/teams/TeamForm.vue
  • frontend/apps/main/src/features/admin/teams/TeamsDataTableColumns.js
  • frontend/apps/main/src/features/admin/templates/TemplateForm.vue
  • frontend/apps/main/src/features/admin/templates/dataTableColumns.js
  • frontend/apps/main/src/features/admin/templates/dataTableDropdown.vue
  • frontend/apps/main/src/features/admin/webhooks/WebhookForm.vue
  • frontend/apps/main/src/features/admin/webhooks/dataTableColumns.js
  • frontend/apps/main/src/features/admin/webhooks/dataTableDropdown.vue
  • frontend/apps/main/src/features/command/CommandBox.vue
  • frontend/apps/main/src/features/contact/ContactNotes.vue
  • frontend/apps/main/src/features/contact/ContactsList.vue
  • frontend/apps/main/src/features/conversation/Conversation.vue
  • frontend/apps/main/src/features/conversation/MacroActionsPreview.vue
  • frontend/apps/main/src/features/conversation/ReplyBox.vue
  • frontend/apps/main/src/features/conversation/ReplyBoxContent.vue
  • frontend/apps/main/src/features/conversation/list/ConversationListItem.vue
  • frontend/apps/main/src/features/conversation/list/ConversationListItemSkeleton.vue
  • frontend/apps/main/src/features/conversation/message/MessageBubble.vue
  • frontend/apps/main/src/features/conversation/message/MessageList.vue
  • frontend/apps/main/src/features/conversation/message/MessagesSkeleton.vue
  • frontend/apps/main/src/features/conversation/message/attachment/BubbleAttachmentItem.vue
  • frontend/apps/main/src/features/conversation/message/attachment/ReplyBoxAttachmentPreview.vue
  • frontend/apps/main/src/features/conversation/sidebar/ConversationSideBar.vue
  • frontend/apps/main/src/features/conversation/sidebar/ConversationSideBarContact.vue
  • frontend/apps/main/src/features/conversation/sidebar/ConversationSideBarPageVisits.vue
  • frontend/apps/main/src/features/conversation/sidebar/CopilotPanel.vue
  • frontend/apps/main/src/features/conversation/sidebar/CustomAttributes.vue
  • frontend/apps/main/src/features/conversation/sidebar/PreviousConversations.vue
  • frontend/apps/main/src/features/search/SearchResults.vue
  • frontend/apps/main/src/features/sla/SlaBadge.vue
  • frontend/apps/main/src/features/view/ViewForm.vue
  • frontend/apps/main/src/layouts/admin/AdminSplitLayout.vue
  • frontend/apps/main/src/views/admin/agents/AgentList.vue
  • frontend/apps/main/src/views/admin/ai/AISuggestions.vue
  • frontend/apps/main/src/views/admin/ai/CreateOrEditAssistant.vue
  • frontend/apps/main/src/views/admin/automations/CreateOrEditRule.vue
  • frontend/apps/main/src/views/admin/context-links/CreateEditContextLink.vue
  • frontend/apps/main/src/views/admin/custom-attributes/CustomAttributes.vue
  • frontend/apps/main/src/views/admin/inbox/InboxList.vue
  • frontend/apps/main/src/views/admin/status/StatusView.vue
  • frontend/apps/main/src/views/admin/tags/TagsView.vue
  • frontend/apps/main/src/views/admin/webhooks/CreateEditWebhook.vue
  • frontend/apps/main/src/views/auth/ResetPasswordView.vue
  • frontend/apps/main/src/views/auth/SetPasswordView.vue
  • frontend/apps/main/src/views/auth/UserLoginView.vue
  • frontend/apps/main/src/views/contact/ContactDetailView.vue
  • frontend/apps/main/src/views/conversation/ConversationDetailView.vue
  • frontend/apps/main/src/views/reports/OverviewView.vue
  • frontend/apps/main/src/views/search/SearchView.vue
  • frontend/apps/widget/src/components/ChatMessages.vue
  • frontend/apps/widget/src/components/ChatTitle.vue
  • frontend/apps/widget/src/components/ConnectionBanner.vue
  • frontend/apps/widget/src/components/HomeExternalLink.vue
  • frontend/apps/widget/src/components/PreChatForm.vue
  • frontend/shared-ui/assets/styles/main.scss
  • frontend/shared-ui/components/ScrollToBottomButton/ScrollToBottomButton.vue
  • frontend/shared-ui/components/StatusDot.vue
  • frontend/shared-ui/components/ui/alert-dialog/AlertDialogAction.vue
  • frontend/shared-ui/components/ui/badge/index.js
  • frontend/shared-ui/components/ui/button/index.js
  • frontend/shared-ui/components/ui/card/Card.vue
  • frontend/shared-ui/components/ui/sidebar/index.js
  • frontend/shared-ui/components/ui/tooltip/TooltipContent.vue
  • frontend/tailwind.config.cjs
  • i18n/en-US.json
  • internal/ai/agent.go
  • internal/ai/ai.go
  • internal/ai/embedding.go
  • internal/ai/knowledgebase.go
  • internal/ai/knowledgebase_test.go
  • internal/ai/openai.go
  • internal/ai/queries.sql
  • internal/ai/tools.go
  • internal/ai/toolstore.go
  • internal/ai/toolstore_test.go
  • internal/aiagent/aiagent.go
  • internal/aiagent/faq.go
  • internal/aiagent/otp.go
  • internal/aiagent/prompt.go
  • internal/aiagent/prompt_test.go
  • internal/aiagent/queries.sql
  • internal/aiagent/tools.go
  • internal/aiagent/worker.go
  • internal/auth/auth.go
  • internal/conversation/conversation.go
  • internal/conversation/message.go
  • internal/conversation/queries.sql
  • internal/image/image.go
  • internal/migrations/v2.6.0.go
  • internal/notification/notification.go
  • internal/oidc/oidc.go
  • internal/stringutil/emailquote.go
  • internal/stringutil/emailquote_test.go
  • internal/stringutil/htmlchunker.go
  • internal/stringutil/htmlchunker_test.go
  • schema.sql
🚧 Files skipped from review as they are similar to previous changes (49)
  • frontend/apps/main/src/components/button/CopyButton.vue
  • frontend/apps/main/src/features/admin/general/GeneralSettingForm.vue
  • frontend/shared-ui/components/ScrollToBottomButton/ScrollToBottomButton.vue
  • frontend/apps/main/src/constants/navigation.js
  • frontend/apps/main/src/features/conversation/list/ConversationListItemSkeleton.vue
  • frontend/apps/widget/src/components/ChatTitle.vue
  • frontend/apps/main/src/views/admin/agents/AgentList.vue
  • frontend/apps/main/src/features/admin/ai/suggestionColumns.js
  • frontend/apps/widget/src/components/ChatMessages.vue
  • frontend/apps/main/src/features/admin/ai/assistantDropdown.vue
  • frontend/apps/main/src/views/admin/ai/AISuggestions.vue
  • internal/ai/queries.sql
  • frontend/apps/main/src/features/admin/sla/SLAForm.vue
  • internal/conversation/queries.sql
  • frontend/apps/widget/src/components/PreChatForm.vue
  • frontend/apps/main/src/features/admin/ai/snippetColumns.js
  • frontend/apps/main/src/features/admin/ai/assistantColumns.js
  • frontend/apps/main/src/features/admin/ai/snippetDropdown.vue
  • frontend/apps/main/src/features/conversation/Conversation.vue
  • frontend/apps/main/src/features/admin/ai/ToolForm.vue
  • frontend/apps/main/src/features/conversation/ReplyBoxContent.vue
  • internal/auth/auth.go
  • frontend/apps/main/src/features/conversation/sidebar/ConversationSideBar.vue
  • internal/aiagent/prompt.go
  • frontend/apps/main/src/features/admin/ai/AssistantForm.vue
  • cmd/handlers.go
  • frontend/apps/main/src/features/conversation/message/MessageBubble.vue
  • cmd/main.go
  • cmd/aitools.go
  • cmd/init.go
  • internal/ai/knowledgebase.go
  • schema.sql
  • internal/ai/agent.go
  • frontend/apps/main/src/features/conversation/ReplyBox.vue
  • frontend/apps/main/src/features/admin/ai/ProviderForm.vue
  • frontend/apps/main/src/views/reports/OverviewView.vue
  • internal/migrations/v2.6.0.go
  • frontend/apps/main/src/views/admin/ai/CreateOrEditAssistant.vue
  • internal/image/image.go
  • internal/aiagent/faq.go
  • i18n/en-US.json
  • internal/aiagent/aiagent.go
  • internal/ai/tools.go
  • frontend/apps/main/src/features/conversation/sidebar/CopilotPanel.vue
  • internal/ai/openai.go
  • internal/aiagent/tools.go
  • internal/ai/ai.go
  • internal/aiagent/queries.sql
  • internal/conversation/conversation.go

Comment thread frontend/apps/main/src/features/admin/inbox/EmailInboxForm.vue
Comment thread frontend/apps/main/src/features/view/ViewForm.vue
Comment thread frontend/apps/main/src/views/auth/SetPasswordView.vue Outdated
Comment thread frontend/DESIGN.md Outdated
Comment thread internal/aiagent/worker.go
Comment thread internal/stringutil/emailquote.go Outdated
Two AI agent limits were hardcoded in Go: the tool-calling budget per reply
(6) and the number of conversation messages sent to the model as history
(30). Large installs need to tune these, so both are now read from
config.toml as ai_agent.max_steps and ai_agent.max_history_messages.

Defaults are unchanged, so an install that does not set the keys behaves
exactly as before. Values are clamped at the config read layer, 1-20 for
max_steps and 5-100 for history, so a typo cannot burn tokens on a runaway
loop or drop the model's context to nothing.

Also drop the dead re-slice in buildHistory. The message fetch already
limits to max_history_messages and the filter above it only removes
messages, so the second clamp could never fire.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
config.sample.toml (1)

141-147: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Fail closed on SSRF protection for hosted deployments.

ssrf.enabled defaults to false, so the shared outbound dial guard returns nil and AI providers, tools, webhooks, and OIDC clients do not block private, loopback, link-local, or metadata IPs by default. Set it to true by default, or make the single-tenant opt-out explicit and enforceable in the runtime config instead of relying on README-level guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config.sample.toml` around lines 141 - 147, Change the [ssrf] configuration
default so ssrf.enabled is true, ensuring outbound requests fail closed against
private, loopback, link-local, and metadata IPs by default. If single-tenant
deployments must opt out, make that opt-out explicit and enforce it through
runtime configuration rather than comments alone.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@config.sample.toml`:
- Around line 141-147: Change the [ssrf] configuration default so ssrf.enabled
is true, ensuring outbound requests fail closed against private, loopback,
link-local, and metadata IPs by default. If single-tenant deployments must opt
out, make that opt-out explicit and enforce it through runtime configuration
rather than comments alone.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4bc3de65-968a-47df-baf2-bb9dbfbbcd6c

📥 Commits

Reviewing files that changed from the base of the PR and between 6b8a0f9 and 49c5cb3.

📒 Files selected for processing (4)
  • cmd/init.go
  • config.sample.toml
  • internal/aiagent/aiagent.go
  • internal/aiagent/worker.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • cmd/init.go
  • internal/aiagent/aiagent.go
  • internal/aiagent/worker.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/apps/main/src/stores/aiAssistant.js`:
- Around line 9-14: Update loadAssistants and invalidate in the assistant store
to track a request generation, incrementing it on invalidation and capturing the
current generation when a request starts. Only update assistants.value and
loaded.value when the response belongs to the current generation, so stale
in-flight responses cannot re-enable the cache or overwrite newer data.
- Around line 15-17: Update the assistant-loading flow surrounding the catch
block to deduplicate concurrent requests and prevent automatic retries after a
failed load. Track the in-flight promise and ensure all consumers share it, then
either implement explicit retry/reset behavior or mark the failed attempt as
cached by updating the relevant loaded/error state; preserve assistants.value as
an empty list on failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d0f4c653-b12e-4b79-98c7-55c6fa6b57b3

📥 Commits

Reviewing files that changed from the base of the PR and between b569777 and fd21893.

📒 Files selected for processing (4)
  • frontend/apps/main/src/features/admin/ai/assistantDropdown.vue
  • frontend/apps/main/src/features/conversation/sidebar/CopilotPanel.vue
  • frontend/apps/main/src/stores/aiAssistant.js
  • frontend/apps/main/src/views/admin/ai/CreateOrEditAssistant.vue
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/apps/main/src/features/admin/ai/assistantDropdown.vue
  • frontend/apps/main/src/views/admin/ai/CreateOrEditAssistant.vue
  • frontend/apps/main/src/features/conversation/sidebar/CopilotPanel.vue

Comment thread frontend/apps/main/src/stores/aiAssistant.js
Comment thread frontend/apps/main/src/stores/aiAssistant.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/ai/ai.go`:
- Around line 37-40: Remove the duplicate ErrInvalidAPIKey, ErrApiKeyNotSet,
ErrRateLimited, and ErrProviderUnavailable declarations from ai.go, retaining
the canonical package-level definitions already present in openai.go. Ensure all
existing references continue using those shared sentinels.
- Around line 396-403: Update the timeout and provider-unavailable branches in
the surrounding AI provider error handling to return envelope.NetworkError
instead of envelope.GeneralError, preserving their existing localized messages
and nil payloads so upstream failures map to HTTP 504.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ad7cafe3-c0cf-425f-bc7b-d14ef751904b

📥 Commits

Reviewing files that changed from the base of the PR and between e8e2419 and 8db2e34.

📒 Files selected for processing (4)
  • cmd/ai.go
  • i18n/en-US.json
  • internal/ai/ai.go
  • internal/ai/openai.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/ai.go
  • i18n/en-US.json

Comment thread internal/ai/ai.go
Comment thread internal/ai/ai.go
@abhinavxd
abhinavxd merged commit fb4d665 into main Jul 29, 2026
5 checks passed
@abhinavxd
abhinavxd deleted the feat/ai-agent branch July 29, 2026 19:38
@coderabbitai coderabbitai Bot mentioned this pull request Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant