Technical docs
Internal Chat
Overview
Support reference for the current internal chat UI.
Current chat modes
- The current frontend chat peer modes are:
- `aiTeammate`
- `ally`
- Sidebar `Chats` opens the shared chat surface. It is an action item, not navigation to a dedicated `/ai` page.
- `Alt+C` toggles `Chats`.
- Internal chat uses one shared session model for panel, floating, and minimized views.
- On short desktop viewports, docked and floating chats stay open instead of auto-minimizing. Floating chat position and size are normalized to fit the available viewport.
What users can do
- Start a new conversation, open history, rename a conversation, or delete it.
- In the shared `Chats` surface, users first see a history screen across Ally and AI teammates, then open a conversation or start a fresh chat from a target chip.
- The shared panel, floater, and Docs chat host use the same organization-level internal-chat history source.
- History loads `20` conversations per page and automatically loads another page when the user reaches the list sentinel.
- Entering at least `3` search characters automatically searches conversation titles and message content across the current user's Ally and AI teammate chats.
- The target strip uses active teammates and includes the current user's personal Ally.
- Conversation history rows can show a generated conversation icon when the backend provides a valid `conversation_icon_id`; otherwise they render text-only identity without that icon tile.
- Send text, reply to a message, stop generation, and send attachments.
- Use `Enter` to send from the current internal-chat composer.
- Use `Shift+Enter` or `Alt+Enter` to insert a newline without sending.
- Use `@` teammate mentions and `#` storage mentions when mention sources are available.
- `@` mentions resolve from the teammate catalog for the current org. In AI-teammate chat, that list can also include Ally; the currently open teammate is excluded from the mention list. Personal Ally employees are excluded from the mention catalog.
- `#` opens the `Files and folders` source and searches both entry types on demand. Results show the entry name and full storage path.
- In AI teammate chat, `#` results are limited to that teammate's readable shared folders. In personal Ally chat, the search can span the organization's full Storage tree.
- Selected storage mentions preserve whether the reference is a file or folder and are displayed as `#name` in message text.
- Recall previously sent user messages with `ArrowUp` / `ArrowDown` when the composer is otherwise idle.
- Leave feedback on assistant replies:
- `Like`
- `Dislike`
- `Feedback` comment editor
- Failed system-error assistant messages support comment-only feedback.
- Use voice mode for Ally and AI teammate chat when that chat has realtime voice enabled.
Reasoning and display behavior
- The composer shows a `Reasoning` control only when the resolved model supports reasoning.
- User-facing reasoning selections are:
- `Off`
- `Low`
- `Medium`
- `High`
- Reasoning preference is persisted in local storage and scoped by user, org, peer, entity, and conversation. A draft selection can carry forward into the first persisted conversation.
- The reasoning selector is hidden while voice mode is active.
- Assistant messages can persist a preface with reasoning, tool calls, and source cards, so those details survive reloads.
- Message and reasoning text rendered through the shared AI Markdown component supports bare HTTP(S) URLs, inline-code URLs, explicit Markdown links, and angle-bracket autolinks.
- When a bare HTTP(S) URL contains a malformed trailing backtick at a punctuation, symbol, whitespace, or end-of-text boundary, the renderer removes the backtick from the URL and keeps any following punctuation or prose outside the link. Explicit Markdown links, angle-bracket autolinks, and encoded backticks are not rewritten by this repair.
- Same-origin links use in-app navigation. External links open in a new tab.
- Chats that are already at the bottom stay pinned during streaming, thinking updates, and panel/viewport resize. If the user scrolls away, expanding a reasoning/tool section does not force them back to the bottom.
- Message context menus show the exact `Sent` time to all users.
- Organization admins and owners additionally see `Started`, `Ended`, and exact processing `Duration` for completed automated replies when the stored timing is trustworthy.
- Developers additionally see author, role/type, shortened conversation and message identifiers, status/streaming state, and available feedback metadata.
- Processing timing is omitted for streaming, failed, in-progress, placeholder, and system-error messages. For eligible completed replies, the UI uses persisted completion time or derives it from the persisted generation duration.
Mobile and onboarding behavior
- On mobile, the internal-chat panel renders full width above the shared bottom nav instead of as a desktop-sized floating panel.
- Tapping outside the mobile panel overlay closes it.
- The onboarding URL flag `?onboarding=true` auto-opens Ally once the scoped chat state has restored.
- Onboarding Ally bootstrap prefers the latest server conversation once per user/org scope, so users land in the current onboarding thread instead of a blank draft.
- The onboarding floater is positioned bottom-right on desktop when the saved chat mode restores as a floating session.
REST endpoints the current UI uses
- Cross-teammate history for the current user:
- `GET /api/organizations/{orgId}/internal-chat/conversations?page=...&limit=...`
- `GET /api/organizations/{orgId}/internal-chat/conversations/search?q=...&limit=...`
- The list response includes `page`, `limit`, `total`, and `hasMore`. Rows include conversation title/icon/preview fields plus the associated AI employee identity.
- List rows have a last message, belong to the current user through an active internal-chat participant, and exclude deleted conversations and deleted AI employees.
- List and search results are additionally authorization-scoped: the conversation's AI employee must be accessible to the current user (a regular AI teammate granted through Access Groups, or the user's own personal Ally; owners and admins see all regular AI teammates). Removing a user's access to a teammate removes that teammate's conversations from history even when the user was a participant.
- Ally employee resolution:
- `GET /api/organizations/{orgId}/employees/ally`
- The current frontend resolves the signed-in user's personal Ally employee first, then uses that employee ID with the shared employee chat routes below.
- AI teammate and Ally chat:
- `GET /api/organizations/{orgId}/employees/{employee_id}/messages`
- `POST /api/organizations/{orgId}/employees/{employee_id}/messages`
- `POST /api/organizations/{orgId}/employees/{employee_id}/conversations/new`
- `GET /api/organizations/{orgId}/employees/{employee_id}/conversations`
- `GET /api/organizations/{orgId}/employees/{employee_id}/conversations/search`
- `PATCH /api/organizations/{orgId}/employees/{employee_id}/conversations/{conversation_id}/title`
- `DELETE /api/organizations/{orgId}/employees/{employee_id}/conversations/{conversation_id}`
- `POST /api/organizations/{orgId}/employees/{employee_id}/conversations/{conversation_id}/stop-generation` with `{ "run_id": "<uuid>" }`
- Stop-generation behavior:
- The request targets one exact conversation and generation run.
- A run mismatch returns `404 Generation not found`.
- A run that is already terminal returns `200` with `Generation is not in progress`.
- After the server accepts an abort, the frontend disables repeated stop requests until the tracked run reaches its terminal state.
- Capability bootstrap used by the current frontend:
- `GET /api/init/internal-chat-capabilities`
- Current response only exposes Ally model availability.
Feedback and realtime
- Canonical internal-chat feedback endpoint:
- `PUT /api/organizations/{orgId}/conversations/{conversation_id}/messages/{message_id}/feedback`
- Feedback comments are capped at `500` characters.
- Only the conversation owner can update feedback.
- `429` feedback responses are surfaced as rate-limit failures, and `409` is used for messages that do not accept feedback.
- Internal chat subscribes to:
- `internal.message`
- `internal.message_updated`
- `voice_thinking_started`
- `voice_thinking_ended`
- `internal.message_updated` is the channel used to sync feedback edits and other message updates into the transcript.
- Streaming tool activity uses `tool_call_started`, `tool_call_done`, and `tool_call_failed` events. Pending text and reasoning buffers are flushed before a tool event so transcript ordering stays stable.
- Oversized tool payloads can be omitted from websocket events and loaded from the persisted message `content_payload`. The final `message_done` event is authoritative for completed message content.
- An unfinished assistant message remains the durable signal that generation is active after a reload.
- Messages with status `in_progress` are not rendered in the transcript or conversation previews.
Attachments and voice
- Shared chat input currently enforces:
- max `10` attachments per message
- max `10 MB` per attachment
- max `30 MB` total attachments per message
- `Too many attachments` toast when the file-count cap is exceeded
- `File too large` toast when a file exceeds the per-file or total-size cap
- drag/drop overlay text `Drop files here`, or `Attachment limit reached` when the cap is already hit
- Shared chat input currently accepts:
- images
- SQLite database files (`.db3`)
- plain text (`.txt`)
- log files (`.log`)
- Markdown (`.md`, `.markdown`)
- CSV
- XLSX
- DOCX
- XML
- Message attachment URLs returned to authenticated internal chat clients use `/shared/attachments/{organizationId}/attachment/{attachmentId}` instead of direct object-storage URLs.
- Spreadsheet chat attachments are prepared as workflow attachment context for CSV and XLSX when the backend can parse them.
- File attachments included in AI prompt history use run-scoped shared URLs shaped `/shared/attachments/{organizationId}/run/{runId}/attachment/{attachmentId}`; the route returns `File not found` after the workflow run is finished.
- `.txt` and `.log` attachments are normalized to `text/plain` before send.
- `.md` and `.markdown` attachments are normalized to `text/markdown` before send.
- Clipboard-pasted generic image names are rewritten to `screenshot-...`.
- Realtime voice session creation for AI teammates and Ally uses:
- `POST /chat/realtime/employee/create-session/{organization_id}/{employee_id}`
- Ally uses the personal employee ID returned by `GET /api/organizations/{orgId}/employees/ally`.
- Voice session creation returns a `conversation_id` so the chat UI can bind the realtime session to the correct conversation.
- Employee voice session creation requires exactly one of:
- `conversation_id`
- `create_new_conversation`