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.
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
Livechat:
- Add a live widget preview that renders in the inbox settings help rail.
- Warn when the primary or header colors are too close to the background.
- Require gradient/image background values and notice banner text when enabled.
- Show a default launcher logo in the widget when none is set, matching the preview.
- Drop the Beta badge from the livechat channel.
General settings:
- Add "set agents away on login". Agents log in as away and must go online manually.
- Add "show subject in conversation list" toggle.
Other:
- Add a keyboard shortcuts dialog from the user menu.
- Show a "snoozed until" badge on snoozed conversations.
- Contact search now matches emails partially instead of exact.
- Give outgoing message bubbles a secondary background.
Address CodeRabbit review: the query deleted by message uuid alone, so an agent
with access to one conversation could delete a private note from another by
pairing an accessible cuuid with a foreign message uuid. The delete is now
scoped via conversation_id = (SELECT id FROM conversations WHERE uuid = $2), and
DeletePrivateMessage / the handler pass the conversation uuid alongside the
message uuid.
[Used Claude Code 🤖]
Adds DELETE /api/v1/conversations/{cuuid}/messages/{uuid} for removing private
notes via the API. Uses a dedicated `delete-private-message` query/stmt
(separate from the generic `delete-message` used internally by continuity) so
only messages with private=true can be deleted — sent/incoming messages are
protected. Returns 404 if the message doesn't exist or isn't private.
Useful for API consumers that create private notes programmatically and need to
clean them up (e.g. replacing an outdated note before adding a new one).
[Used Claude Code 🤖]
The autoassigner fetched the unassigned list once, then looped and
assigned each one. If the conversation was reassigned while that slow
batch was still running, the autoassigner's write still landed and
overrode it, because the UPDATE had no guard.
Adds an atomic claim that only assigns when the conversation is still
unassigned AND still in the team the user was picked from. A manual
user assignment or a team change between the list read and the claim
now makes the claim a no-op, and the side-effects (webhook, automation,
notification) are skipped. Manual assignment keeps its unconditional
override path.
Agents can download a full conversation as a .txt file from the
header menu. The conversation sidebar now shows the CSAT rating and
feedback once the customer responds.
Convs and messages now push the full list-row payload to perm-authorized agents on create/assign/message, so livechat returning customers bubble up instantly without waiting for the timer refresh. Background list refresh bumped 30s -> 60s as a drift safety net.
Targets broadcasts via per-tab list and open subs (validated at subscribe time)
instead of broadcasting message uuid to all agents. Adds frontend list typing, throttled sound,
and many perf improvements to reduce cpu memory & database queries.
Other fixes: Enforce message-to-conversation binding in handleGetMessage
- Introduced a new category field for conversation statuses to classify them as 'open', 'waiting', or 'resolved'. Which allows max assignment limits in team to work with custom statuses as well.
- Refactor widget.js.
- Fix possible race conditions in inbox reload logic.
- Reload only the updated inbox when an inbox is updated / created not all.
- Retain last inbox while navigatin back from admin -> inbox
- Continuity email layout fixes and improvments.
- Replace haikunator with email local part for visitor names, fallback to "Visitor"
- Add optional website_url config field for "go to chat" link in continuity email footer
- Remove unused / unncessary i18n keys.
- fix duplicate initialization guard
- notification bell for agents
- Refactor and clean up dead code
- Fixes to pre chat form
- Add new column country to users table
- Update ratelimit pkg to be generic
- fix: live chat email inbox not being initialized.
- make launcher logo take entire launcher space.
- New columns last_interaction_sender_id and last_message_sender_id to track who sent last message in conversation
feat: make data table item names clickable.
fix(conversation): focus reply box editor on conversation change
fix(conversation): focus editor on tab change in ReplyBoxContent
fix(conversation): update conversation list item to use router-link
fix(conversation): update message bubble to use router-link for author
fix(conversation): update last interaction fields on message insert
fix(conversation): add last interaction fields to conversation model
fix(conversation): update SQL queries to include last interaction fields
feat(migrations): add migration for last interaction fields in conversations
fix(schema): update schema to include last interaction fields in conversations