diff --git a/content/docs/configuration/authentication/OAuth2-OIDC/azure.mdx b/content/docs/configuration/authentication/OAuth2-OIDC/azure.mdx
index 901861fb0..dff50398e 100644
--- a/content/docs/configuration/authentication/OAuth2-OIDC/azure.mdx
+++ b/content/docs/configuration/authentication/OAuth2-OIDC/azure.mdx
@@ -184,7 +184,7 @@ SHAREPOINT_PICKER_GRAPH_SCOPE=Files.Read.All
### Usage
When properly configured:
-1. Users will see "From SharePoint" option in the file attachment menu
+1. Users will see a **From SharePoint** option in the **Attach** section of the [composer's **+** palette](/docs/features/composer#add-files)
2. Clicking it opens the native SharePoint file picker
3. Users can browse and select files from any SharePoint site or OneDrive they have access to
4. Selected files are downloaded and attached to the conversation
diff --git a/content/docs/configuration/authentication/email.mdx b/content/docs/configuration/authentication/email.mdx
index 27e5eb49e..b1ae82ce9 100644
--- a/content/docs/configuration/authentication/email.mdx
+++ b/content/docs/configuration/authentication/email.mdx
@@ -203,6 +203,52 @@ EMAIL_FROM_NAME=LibreChat
ALLOW_PASSWORD_RESET=true
```
+## Email Address Change
+
+
+ Newer than **v0.8.8**. Available now on LibreChat's `dev` and `canary` branches, and in the next release.
+
+
+Users with a local (email and password) account can change their registered email address themselves from **Settings > Account**. The feature is on by default and needs working email delivery (Mailgun or SMTP, configured above): without it, the option is hidden.
+
+### User flow
+
+1. In **Settings > Account**, next to **Email address**, the user selects **Change**.
+2. In the **Change email address** dialog, they enter the **New email address** and their **Current password**, then select **Send verification link**.
+3. LibreChat first sends a security notice ("Email change requested") to the current address, with the requested address and the request IP. It sends this before checking the password, so the account owner hears about every attempt, including rejected ones. If the request passes the checks below, a verification link goes to the new address.
+4. The address changes only when the link is opened. Both the old and new addresses then receive a "Your email address was changed" notice, and the new address is marked as verified.
+
+The request is rejected when the password is wrong, the new address equals the current one, the new address already belongs to another account, or its domain is not in [`registration.allowedDomains`](/docs/configuration/librechat_yaml/object_structure/registration#alloweddomains). Accounts that sign in through OAuth, OpenID Connect, SAML, or LDAP cannot use this flow.
+
+The verification link is single-use and expires after `tokenTTLSeconds` (15 minutes by default). It also stops working if the password or email of the account changes before it is opened. Completing a change invalidates any password reset links issued for the previous address.
+
+### Configuration
+
+
+
+In `librechat.yaml`, the `emailChange` block takes precedence over `ALLOW_EMAIL_CHANGE` and also sets the link lifetime:
+
+```yaml filename="librechat.yaml"
+emailChange:
+ enabled: true
+ tokenTTLSeconds: 900 # 60 to 86400, default 900 (15 minutes)
+```
+
+Each field falls back independently: `enabled` to `ALLOW_EMAIL_CHANGE` and then `true`; `tokenTTLSeconds` to `900`. Rate limits for requesting a change and for opening links are set under [`rateLimits.emailChange` and `rateLimits.emailChangeConfirm`](/docs/configuration/librechat_yaml/object_structure/config#ratelimits), or with the matching [environment variables](/docs/configuration/dotenv#email-change-rate-limiting).
+
+
+Set `ALLOW_EMAIL_CHANGE=false` (or `emailChange.enabled: false`) until every node runs a version that includes this feature. Older nodes issue and accept password reset links that are not bound to an address, so a link held by the previous owner of an address could outlive the change and reset the renamed account.
+
+
## Troubleshooting
### Mailgun Issues
diff --git a/content/docs/configuration/authentication/index.mdx b/content/docs/configuration/authentication/index.mdx
index 86e92576f..b9dae82aa 100644
--- a/content/docs/configuration/authentication/index.mdx
+++ b/content/docs/configuration/authentication/index.mdx
@@ -60,6 +60,68 @@ Quick Tips:
alt="User registration screen"
/>
+Related sign-in options for local accounts:
+
+- [Passkeys](/docs/configuration/authentication/passkeys): passwordless sign-in with a device screen lock or security key (`ALLOW_PASSKEY_LOGIN`).
+- [Email address change](/docs/configuration/authentication/email#email-address-change): lets users change their registered email from **Settings > Account** (`ALLOW_EMAIL_CHANGE`, on by default).
+
+## Required Two-Factor Authentication
+
+
+ Newer than **v0.8.8**. Available now on LibreChat's `dev` and `canary` branches, and in the next release.
+
+
+Users can always turn on two-factor authentication (TOTP) themselves from **Settings > Account**. To make it mandatory, set:
+
+```bash filename=".env"
+ENFORCE_TWO_FACTOR_AUTHENTICATION=true
+```
+
+**Who it applies to:** local (email and password) and LDAP accounts. Accounts that sign in through OAuth, OpenID Connect, or SAML are not affected, because their identity provider is responsible for MFA. A federated account that tries to sign in with a password while enforcement is on is told to use its identity provider instead.
+
+**What users see:** a user without 2FA who signs in (with a password, LDAP credentials, or a passkey) gets no session until setup is complete. They land on a **Two-Factor Authentication Required** screen ("Your administrator requires two-factor authentication. Set it up before continuing."), scan a QR code with an authenticator app, enter a code to verify, and save their backup codes. Users who are already signed in are moved into the same setup the next time their session is checked or refreshed, and access tokens issued before enrollment stop working. The setup session lasts 10 minutes; if it expires, the user returns to the login page and starts again.
+
+**After enrollment:** the **Disable 2FA** control in **Settings > Account** shows **Required by administrator**, and the server rejects attempts to disable 2FA while the policy is on. Users can still regenerate backup codes.
+
+
+
+
+`ENFORCE_TWO_FACTOR_AUTHENTICATION` and the `TWO_FACTOR_TEMP_*` and `TWO_FACTOR_SETUP_*` limits are process-wide `.env` settings. They apply before a request or tenant configuration is available, so they cannot be set in `librechat.yaml` or per tenant. Set the same values on every replica. The separate budget for managing 2FA from settings is [`rateLimits.twoFactorManagement`](/docs/configuration/librechat_yaml/object_structure/config#ratelimits).
+
+
## Session Expiry and Refresh Token
- Default values: session expiry: 15 minutes, refresh token expiry: 7 days
diff --git a/content/docs/configuration/authentication/meta.json b/content/docs/configuration/authentication/meta.json
index caf654b81..b8fdeaf7f 100644
--- a/content/docs/configuration/authentication/meta.json
+++ b/content/docs/configuration/authentication/meta.json
@@ -1,5 +1,5 @@
{
"title": "Authentication",
"icon": "Lock",
- "pages": ["email", "ldap", "OAuth2-OIDC", "SAML"]
+ "pages": ["email", "passkeys", "ldap", "OAuth2-OIDC", "SAML"]
}
diff --git a/content/docs/configuration/authentication/passkeys.mdx b/content/docs/configuration/authentication/passkeys.mdx
new file mode 100644
index 000000000..787e42d25
--- /dev/null
+++ b/content/docs/configuration/authentication/passkeys.mdx
@@ -0,0 +1,142 @@
+---
+title: Passkeys
+icon: KeyRound
+description: Let users sign in without a password using a device screen lock or a security key (WebAuthn). Covers enabling passkeys, the relying party settings, enrollment limits, and how passkeys interact with two-factor authentication.
+---
+
+Passkeys let users with a local (email and password) account sign in with their device screen lock, a password manager, or a hardware security key instead of typing a password. LibreChat implements them with WebAuthn. They are off by default.
+
+
+ Newer than **v0.8.8**. Available now on LibreChat's `dev` and `canary` branches, and in the next release.
+
+
+Use passkeys when you want a phishing-resistant, passwordless sign-in for local accounts. Accounts that sign in through OAuth, OpenID Connect, SAML, or LDAP keep using their provider and cannot add passkeys.
+
+## Enable passkeys
+
+
+
+
+**Serve LibreChat over HTTPS.** Browsers only allow WebAuthn in a secure context. `http://localhost` is exempt, so local testing works without TLS.
+
+
+
+
+**Turn the feature on in `.env`.** For a standard deployment, this is the only required setting. The relying party ID and allowed origins are derived from `DOMAIN_CLIENT` and `DOMAIN_SERVER`.
+
+```bash filename=".env"
+ALLOW_PASSKEY_LOGIN=true
+```
+
+
+
+
+**Pin the relying party ID (recommended).** Each passkey is permanently bound to the RP ID it was created under. If `DOMAIN_CLIENT` might ever change, set `PASSKEY_RP_ID` now so existing passkeys keep working.
+
+```bash filename=".env"
+PASSKEY_RP_ID=chat.example.com
+```
+
+
+
+
+**Restart LibreChat.** The login page shows **Sign in with a passkey**, and **Settings > Account** shows a **Passkeys** section.
+
+
+
+
+## Configuration reference
+
+
+
+### Per-user limit in `librechat.yaml`
+
+You can also set the enrollment cap in `librechat.yaml`:
+
+```yaml filename="librechat.yaml"
+passkeys:
+ perUserMax: 10
+```
+
+The cap resolves in this order: `passkeys.perUserMax` in `librechat.yaml`, then `MAX_PASSKEYS_PER_USER`, then the default of `20`. Values outside `1` to `100` are ignored and the next source is used. When a user reaches the cap, **Settings > Account** shows "You have reached the maximum number of passkeys". See [`passkeys`](/docs/configuration/librechat_yaml/object_structure/config#passkeys) in the config reference.
+
+## What users see
+
+**Adding a passkey**
+
+1. Open **Settings > Account**, find **Passkeys**, and select **Manage**.
+2. Select **Add passkey**.
+3. Enter the account password in the **Confirm your password** dialog. A passkey is a complete sign-in on its own, so LibreChat asks for the password before creating one.
+4. Complete the browser or device prompt. The new passkey appears in the list with a default name such as **This device**, **Phone or tablet**, or **Security key**.
+
+Each entry shows when it was added and last used, and a **Synced** badge when the authenticator reports that the passkey is backed up (for example, by a password manager). Users can rename a passkey, and removing one (**Remove passkey**) also asks for the account password.
+
+**Signing in**
+
+On the login page, users select **Sign in with a passkey** and approve the device prompt. If the account has two-factor authentication enabled, LibreChat still asks for the 2FA code afterward, exactly as it does after a password sign-in.
+
+## Behavior and caveats
+
+- **Local accounts only.** Passkey enrollment and sign-in are limited to local accounts. Accounts created through an identity provider or LDAP must keep authenticating through that provider.
+- **Works with email login disabled.** The **Sign in with a passkey** button and its routes depend only on `ALLOW_PASSKEY_LOGIN`. With `ALLOW_EMAIL_LOGIN=false`, existing local users can still sign in with passkeys they enrolled earlier.
+- **Two-factor authentication.** Passkey sign-in goes through the same 2FA gate as password sign-in. With [`ENFORCE_TWO_FACTOR_AUTHENTICATION=true`](/docs/configuration/authentication#required-two-factor-authentication), a user who has not enrolled in 2FA is sent to the setup flow after signing in with a passkey.
+- **Changing the RP ID orphans passkeys.** Passkeys created under one RP ID never work under another. Changing `PASSKEY_RP_ID`, or changing `DOMAIN_CLIENT` while `PASSKEY_RP_ID` is unset, leaves every existing passkey unusable; users must sign in another way and enroll again.
+- **Origins must match.** If users reach LibreChat on an origin not covered by `PASSKEY_ORIGINS` (or the derived defaults), the browser shows "Passkeys are not available on this domain". Add every public origin to `PASSKEY_ORIGINS` when you serve LibreChat on more than one.
+- **Password reset removes passkeys.** A completed password reset signs the account out everywhere and deletes all of its passkeys, so a passkey enrolled before the reset can no longer be used to get in. Users enroll again afterward.
diff --git a/content/docs/configuration/dotenv.mdx b/content/docs/configuration/dotenv.mdx
index 5c15db06b..cd3c3feec 100644
--- a/content/docs/configuration/dotenv.mdx
+++ b/content/docs/configuration/dotenv.mdx
@@ -1954,6 +1954,70 @@ Prevents brute force attacks and spam registrations by limiting login attempts a
The login-attempt budget is shared by the local login API and top-level social or federated OAuth navigations from the same IP. A rejected API login receives the existing JSON `429` response. A rate-limited or banned `/oauth/*` browser navigation returns to `/login?redirect=false` with a localized error code instead of rendering a JSON document; `redirect=false` prevents an automatic OpenID redirect from immediately entering the limiter again.
+#### Two-factor authentication rate limiting
+
+
+
+These are process-wide settings that apply before request or tenant configuration is available. See [Required Two-Factor Authentication](/docs/configuration/authentication#required-two-factor-authentication).
+
+#### Passkey rate limiting
+
+
+
#### Password reset and email verification rate limiting
LibreChat applies separate IP-based limits to requesting an email and submitting the token from that email. This prevents repeated token guesses without forcing deployments to use the same limit for email delivery and token validation.
@@ -2011,6 +2075,53 @@ LibreChat applies separate IP-based limits to requesting an email and submitting
]}
/>
+#### Email change rate limiting
+
+Limits [email address change](/docs/configuration/authentication/email#email-address-change) requests per signed-in user, and verification link openings per IP and per account.
+
+> Note: These can also be configured via `librechat.yaml` in the `rateLimits.emailChange` and `rateLimits.emailChangeConfirm` sections. A value set in `librechat.yaml` takes precedence over the environment variable.
+
+
+
#### Score for each violation
Note: These can also be configured via `librechat.yaml` in the `rateLimits.conversationsImport` section. A value set in `librechat.yaml` takes precedence over the environment variable.
+
> Note: You can utilize both limiters, but default is to limit by IP only.
##### IP Limiter:
@@ -2329,7 +2460,7 @@ Limits how often users can fork conversations to prevent abuse.
Limits how often users can upload files to prevent abuse.
-> Note: These can also be configured via `librechat.yaml` in the `rateLimits.fileUploads` section.
+> Note: These can also be configured via `librechat.yaml` in the `rateLimits.fileUploads` section. A value set in `librechat.yaml` takes precedence over the environment variable.
##### IP Limiter:
@@ -2373,7 +2504,7 @@ Limits how often users can upload files to prevent abuse.
Limits how often users can use Text-to-Speech to prevent abuse.
-> Note: These can also be configured via `librechat.yaml` in the `rateLimits.tts` section.
+> Note: These can also be configured via `librechat.yaml` in the `rateLimits.tts` section. A value set in `librechat.yaml` takes precedence over the environment variable.
##### IP Limiter:
@@ -2417,7 +2548,7 @@ Limits how often users can use Text-to-Speech to prevent abuse.
Limits how often users can use Speech-to-Text to prevent abuse.
-> Note: These can also be configured via `librechat.yaml` in the `rateLimits.stt` section.
+> Note: These can also be configured via `librechat.yaml` in the `rateLimits.stt` section. A value set in `librechat.yaml` takes precedence over the environment variable.
##### IP Limiter:
@@ -2503,7 +2634,9 @@ see: **[Authentication System](/docs/configuration/authentication)**
All authentication settings in this section should be configured in your `.env` file, not in the
`librechat.yaml` file or `docker-compose.override.yml`. The `docker-compose.override.yml` file is
only used to mount volumes and set environment variables for Docker, while the `librechat.yaml`
- file is used for custom endpoints and other application settings.
+ file is used for custom endpoints and other application settings. The exceptions are
+ `ALLOW_EMAIL_CHANGE` and `MAX_PASSKEYS_PER_USER`, which `emailChange` and `passkeys` in
+ `librechat.yaml` override when set.
- General Settings:
@@ -2558,6 +2691,18 @@ see: **[Authentication System](/docs/configuration/authentication)**
'Set to true to allow users to log in without verifying their email address. If set to false, users will be required to verify their email before logging in.',
'ALLOW_UNVERIFIED_EMAIL_LOGIN=true',
],
+ [
+ 'ALLOW_EMAIL_CHANGE',
+ 'boolean',
+ 'Allow local-account users to change their email address from Settings > Account. Requires email delivery. Enabled by default if omitted. emailChange.enabled in librechat.yaml takes precedence. Keep it false during multi-node rollouts until every node supports it.',
+ '# ALLOW_EMAIL_CHANGE=true',
+ ],
+ [
+ 'ENFORCE_TWO_FACTOR_AUTHENTICATION',
+ 'boolean',
+ 'Require local and LDAP accounts to enroll in 2FA before a full session is issued. Federated identity providers keep their own MFA policies.',
+ 'ENFORCE_TWO_FACTOR_AUTHENTICATION=false',
+ ],
[
'MIN_PASSWORD_LENGTH',
'number',
@@ -2567,6 +2712,8 @@ see: **[Authentication System](/docs/configuration/authentication)**
]}
/>
+See [Email Address Change](/docs/configuration/authentication/email#email-address-change) for `ALLOW_EMAIL_CHANGE` and [Required Two-Factor Authentication](/docs/configuration/authentication#required-two-factor-authentication) for `ENFORCE_TWO_FACTOR_AUTHENTICATION`.
+
> **Quick Tip:** Even with registration disabled, add users directly to the database using `npm run create-user`.
> **Quick Tip:** With registration disabled, you can delete a user with `npm run delete-user email@domain.com`.
@@ -2597,6 +2744,45 @@ see: **[Authentication System](/docs/configuration/authentication)**
- For more information: **[Refresh Token](https://github.com/LibreChat-AI/LibreChat/pull/927)**
+- Passkey Settings:
+
+Passkey (WebAuthn) sign-in for local accounts. Requires HTTPS in production; `localhost` is exempt. See [Passkeys](/docs/configuration/authentication/passkeys).
+
+ Account.
+# Omitted, these fall back to ALLOW_EMAIL_CHANGE and a 15-minute link.
+# emailChange:
+# enabled: true
+# tokenTTLSeconds: 900
+
+# Passkey (WebAuthn) enrollment limits.
+# Omitted, perUserMax falls back to MAX_PASSKEYS_PER_USER, then 20.
+# passkeys:
+# perUserMax: 20
+
# Example Registration Object Structure (optional)
registration:
socialLogins: ['github', 'google', 'discord', 'openid', 'facebook']
@@ -488,6 +499,17 @@ registration:
# ipWindowInMinutes: 60 # Rate limit window for conversation imports per IP
# userMax: 50
# userWindowInMinutes: 60 # Rate limit window for conversation imports per user
+# # Asking for a change of the registered email, per authenticated user.
+# emailChange:
+# userMax: 3
+# userWindowInMinutes: 2
+# # Opening a verification link. The endpoint is unauthenticated, so the source
+# # address is bounded as well as the account the link names.
+# emailChangeConfirm:
+# ipMax: 20
+# ipWindowInMinutes: 2
+# userMax: 2
+# userWindowInMinutes: 2
# Source-aware content filters are disabled when omitted. `filters` is loaded
# only from the base config; role, group, and user overrides cannot change it.
diff --git a/content/docs/configuration/librechat_yaml/object_structure/config.mdx b/content/docs/configuration/librechat_yaml/object_structure/config.mdx
index 59bb125dc..876a7289f 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/config.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/config.mdx
@@ -28,6 +28,57 @@ icon: Settings
]}
/>
+## projects
+
+_Newer than v0.8.8._
+
+**Key:**
+
+
+
+**Subkeys:**
+
+
+
+See [Projects](/docs/features/projects). Unknown keys inside `projects` fail validation.
+
+```yaml filename="projects"
+projects:
+ maxFiles: 50
+ maxInstructionsLength: 16000
+ maxDescriptionLength: 1000
+```
+
## permissions
Controls retry behavior for concurrent access-control-list writes.
@@ -151,6 +202,23 @@ See: [Content Filter Object Structure](/docs/configuration/librechat_yaml/object
See: [Legacy messageFilter](/docs/configuration/librechat_yaml/object_structure/message_filter#legacy-messagefilter)
+## fileListLimit
+
+_Newer than v0.8.8._
+
+
+
+Requests without a `limit` are not affected. The composer's recent files list ([`interface.composerRecentFiles`](/docs/configuration/librechat_yaml/object_structure/interface)) is capped at 100, this setting's default, so the palette never asks for more rows than a default deployment returns.
+
## fileStrategy
- **Options**: "local" | "firebase" | "s3" | "azure_blob" | "cloudfront"
@@ -399,6 +467,50 @@ Set `secureImageLinks: false` only as a compatibility opt-out for deployments th
]}
/>
+## conversationList
+
+_Newer than v0.8.8._
+
+**Key:**
+
+
+
+**Subkeys:**
+
+
+
+See [Navigation](/docs/features/navigation) for the conversation list filters. Values outside these ranges fail config validation like any other invalid key. An invalid value set through an Admin Panel override is ignored with a warning, and the defaults apply.
+
+```yaml filename="conversationList"
+conversationList:
+ maxEndpointFilters: 50
+ maxEndpointNameLength: 128
+```
+
## ocr
**Key:**
@@ -742,9 +854,29 @@ see: [File Config Object Structure](/docs/configuration/librechat_yaml/object_st
],
['stt', 'Object', 'Configures rate limits specifically for speech-to-text (stt) requests', ''],
['tts', 'Object', 'Configures rate limits specifically for text-to-speech (tts) requests', ''],
+ [
+ 'mcpApps',
+ 'Object',
+ 'Per-user limits for MCP App browser routes, in a fixed one-minute window.',
+ '',
+ ],
+ [
+ 'emailChange',
+ 'Object',
+ 'Limits requests to change the registered email address, per signed-in user.',
+ '',
+ ],
+ [
+ 'emailChangeConfirm',
+ 'Object',
+ 'Limits openings of email change verification links, per IP and per account.',
+ '',
+ ],
]}
/>
+**Precedence:** at startup, LibreChat writes each value set under `fileUploads`, `conversationsImport`, `tts`, `stt`, `agentEvents`, `emailChange`, and `emailChangeConfirm` into the matching environment variable (prefixes `FILE_UPLOAD`, `IMPORT`, `TTS`, `STT`, `AGENT_EVENT`, `EMAIL_CHANGE`, and `EMAIL_CHANGE_CONFIRM`, with suffixes `_IP_MAX`, `_IP_WINDOW`, `_USER_MAX`, and `_USER_WINDOW`). A YAML value therefore overrides the environment variable, and a key you leave out keeps the environment value or the built-in default. `twoFactorManagement` and `mcpApps` have no environment equivalents.
+
**twoFactorManagement Subkeys:**
+**mcpApps Subkeys:**
+
+
+
+**emailChange Subkeys:**
+
+
+
+**emailChangeConfirm Subkeys:**
+
+
+
+The confirmation endpoint is unauthenticated, so it is bounded by source address as well as by the account the link names. See [Email Address Change](/docs/configuration/authentication/email#email-address-change).
+
- **Example**:
```yaml filename="rateLimits"
rateLimits:
@@ -881,6 +1064,17 @@ This admission bucket is separate from normal message execution limits. The dura
ipWindowInMinutes: 1
userMax: 50
userWindowInMinutes: 1
+ mcpApps:
+ resourcesPerMinute: 120
+ toolCallsPerMinute: 60
+ emailChange:
+ userMax: 3
+ userWindowInMinutes: 2
+ emailChangeConfirm:
+ ipMax: 20
+ ipWindowInMinutes: 2
+ userMax: 2
+ userWindowInMinutes: 2
```
## registration
@@ -908,6 +1102,82 @@ see also:
- [alloweddomains](/docs/configuration/librechat_yaml/object_structure/registration#alloweddomains),
- [Registration Object Structure](/docs/configuration/librechat_yaml/object_structure/registration)
+## emailChange
+
+_Newer than v0.8.8._
+
+**Key:**
+
+ Account, and how long verification links last.',
+ '',
+ ],
+ ]}
+/>
+
+**Subkeys:**
+
+
+
+Each field set here takes precedence over its environment fallback. Email delivery must be configured for the option to appear. See [Email Address Change](/docs/configuration/authentication/email#email-address-change).
+
+```yaml filename="emailChange"
+emailChange:
+ enabled: true
+ tokenTTLSeconds: 900
+```
+
+## passkeys
+
+_Newer than v0.8.8._
+
+**Key:**
+
+
+
+**Subkeys:**
+
+
+
+Passkeys themselves are enabled with `ALLOW_PASSKEY_LOGIN` in `.env`. See [Passkeys](/docs/configuration/authentication/passkeys).
+
+```yaml filename="passkeys"
+passkeys:
+ perUserMax: 20
+```
+
## memory
**Key:**
@@ -1149,11 +1419,11 @@ see also:
'Enables or disables the "Run Code" button for Markdown Code Blocks',
'',
],
- ['webSearch', 'Boolean', 'Enables or disables the web search button in the chat interface', ''],
+ ['webSearch', 'Boolean', 'Enables or disables the Web Search tool in the composer palette', ''],
[
'fileSearch',
'Boolean',
- 'Enables or disables the file search button in the chat interface',
+ 'Enables or disables the File Search tool in the composer palette',
'',
],
['fileCitations', 'Boolean', 'Globally enables or disables file citations for all users', ''],
diff --git a/content/docs/configuration/librechat_yaml/object_structure/interface.mdx b/content/docs/configuration/librechat_yaml/object_structure/interface.mdx
index 8cd12438c..8dc0acf36 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/interface.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/interface.mdx
@@ -37,12 +37,18 @@ These are fields under `interface`:
- `autoSubmitFromUrl`
- `customWelcome`
- `codeHighlightThrottleMs`
+- `artifactUndocking`
- `runCode`
- `webSearch`
- `fileSearch`
- `fileCitations`
- `feedback`
+- `replyNotifications`
- `defaultPinnedTools`
+- `composerRecentFiles`
+- `steerArmConfirmationTimeoutMs`
+- `queuedTurnReconciliationTimeoutMs`
+- `queuedSendLockTimeoutMs`
- `peoplePicker`
- `marketplace`
@@ -129,6 +135,7 @@ interface:
fireConcurrency: 5
customWelcome: 'Hey {{user.name}}! Welcome to LibreChat'
codeHighlightThrottleMs: 300
+ artifactUndocking: true
runCode: true
webSearch: true
fileSearch: true
@@ -137,7 +144,6 @@ interface:
defaultPinnedTools:
- artifacts
- execute_code
- - mcp
```
## theme
@@ -312,6 +318,14 @@ interface:
]}
/>
+### Where the policy links appear
+
+On the sign-in and registration screens, published policies appear as a consent sentence: "By continuing, you agree to the Terms of Service and acknowledge the Privacy Policy." If only one policy is published, the sentence names only that one. On the registration screen it sits under the submit button; on the sign-in screen it sits below the sign-in options.
+
+In chat, the policy links appear under the message box on the welcome screen of a new chat, and are hidden once a conversation starts.
+
+A policy whose `externalUrl` is empty or blank is treated as not published and is never shown. When `termsOfService.modalAcceptance` is `true`, the auth screens show plain policy links instead of the consent sentence, because users accept the terms explicitly in the dialog after signing in.
+
## termsOfService
**Key:**
@@ -1059,14 +1073,14 @@ Controls whether the temporary chat feature is available to users. Temporary cha
'temporaryChat',
'Boolean',
'Enables or disables the temporary chat feature.',
- 'When set to `false`, users will not see the option to start temporary chats.',
+ 'When set to false, users will not see the option to start temporary chats, unless retentionMode is "ephemeral", which shows the toggle locked on for everyone.',
],
]}
/>
**Default:** `true`
-**Note:** The retention period for temporary chats can be configured using `temporaryChatRetention`.
+**Note:** The retention period for temporary chats can be configured using `temporaryChatRetention`. Under `retentionMode: "ephemeral"` the toggle is shown, locked on, to every user regardless of this setting or the `TEMPORARY_CHAT` permission.
**Example:**
@@ -1077,7 +1091,7 @@ interface:
## temporaryChatRetention
-The `temporaryChatRetention` configuration allows you to customize how long temporary chats are retained before being automatically deleted.
+The `temporaryChatRetention` configuration allows you to customize how long temporary chats are retained before being automatically deleted. Under `retentionMode: "ephemeral"`, every chat is temporary, so this value is the lifetime of every chat.
**Key:**
@@ -1123,7 +1137,7 @@ interface:
## generalChatRetention
-Controls how long regular chats are retained when `retentionMode` is set to `"all"`.
+Controls how long regular chats are retained when `retentionMode` is set to `"all"`. It is ignored by the other modes; under `"ephemeral"` every chat is temporary and uses `temporaryChatRetention`.
- `retentionMode: "all"` applies retention deadlines beyond temporary chats, including persistent
- agent resource files unless `retainAgentFiles: true` is configured. Confirm your retention policy
- before enabling it.
+ `retentionMode: "all"` and `"ephemeral"` apply retention deadlines beyond temporary chats,
+ including persistent agent resource files unless `retainAgentFiles: true` is configured. Confirm
+ your retention policy before enabling either.
**Example:**
@@ -1179,6 +1201,14 @@ interface:
retentionMode: 'all'
```
+To force every chat to be temporary and never persisted long-term:
+
+```yaml filename="interface / retentionMode (ephemeral)"
+interface:
+ temporaryChatRetention: 24
+ retentionMode: 'ephemeral'
+```
+
## retainAgentFiles
Controls whether persistent agent resource files are exempt from all-data retention.
@@ -1190,7 +1220,7 @@ Controls whether persistent agent resource files are exempt from all-data retent
[
'retainAgentFiles',
'Boolean',
- 'When true, persistent agent resource files do not expire under retentionMode: "all". Non-agent files and message attachments still expire.',
+ 'When true, persistent agent resource files do not expire under retentionMode: "all" or "ephemeral". Non-agent files and message attachments still expire.',
'retainAgentFiles: false',
],
]}
@@ -1200,7 +1230,7 @@ Controls whether persistent agent resource files are exempt from all-data retent
**Notes:**
-- This setting only changes behavior when `retentionMode` is set to `"all"`.
+- This setting only changes behavior when `retentionMode` is set to `"all"` or `"ephemeral"`.
- Set this to `true` when agents should keep their persistent resource files even while conversations, messages, and non-agent files receive retention deadlines.
**Example:**
@@ -1286,6 +1316,34 @@ interface:
codeHighlightThrottleMs: 300
```
+## artifactUndocking
+
+_Newer than v0.8.8._ Controls whether users can move the [Artifacts](/docs/features/artifacts) pane into its own browser window.
+
+**Key:**
+
+
+
+**Default:** `true`
+
+Setting it to `false` hides the button for new undocks. A pane that is already in its own window keeps its **Dock back to panel** button, so users can always bring it back.
+
+**Example:**
+
+```yaml filename="interface / artifactUndocking"
+interface:
+ artifactUndocking: false
+```
+
## runCode
Enables/disables the "Run Code" button for Markdown Code Blocks. More info on the [LibreChat Code Interpreter API](/docs/features/code_interpreter)
@@ -1313,7 +1371,7 @@ interface:
## webSearch
-Enables/disables the web search button in the chat interface. More info on [Web Search Configuration](/docs/configuration/librechat_yaml/object_structure/web_search)
+Enables/disables the **Web Search** tool in the [composer](/docs/features/composer) palette. More info on [Web Search Configuration](/docs/configuration/librechat_yaml/object_structure/web_search)
**Note:** This setting does not disable the [Agents Web Search capability](/docs/features/agents#agent-capabilities). To disable the Agents capability, see the [Agents endpoint configuration](/docs/configuration/librechat_yaml/object_structure/agents#capabilities) instead.
@@ -1323,7 +1381,7 @@ Enables/disables the web search button in the chat interface. More info on [Web
@@ -1338,7 +1396,7 @@ interface:
## fileSearch
-Enables/disables the file search (for RAG API usage via tool) button in the chat interface
+Enables/disables the **File Search** tool (RAG API usage via tool) in the [composer](/docs/features/composer) palette.
**Note:** This setting does not disable the [Agents File Search Capability](/docs/features/agents#file-search). To disable the Agents Capability, see the [Agents Endpoint configuration](/docs/configuration/librechat_yaml/object_structure/agents) instead.
@@ -1348,7 +1406,7 @@ Enables/disables the file search (for RAG API usage via tool) button in the chat
@@ -1429,6 +1487,51 @@ interface:
feedback: false
```
+## replyNotifications
+
+_Newer than v0.8.8._ Controls which unread-reply alerts users are allowed to turn on. When a reply finishes while a user is looking at another chat, LibreChat marks that chat as unread. Users then choose their own alerts in **Settings > General > Notifications**; these options only decide which of those alerts are offered.
+
+The unread dots in the chat list and the **Mark as unread** action are always available and are not affected by this setting.
+
+**Key:**
+
+
+
+**Sub-keys:**
+
+
+
+Setting `tabBadge`, `desktop` or `sound` to `false` hides that option from users' settings. What each user turns on is stored per device. See [Settings](/docs/features/settings) for the user side.
+
+**Example:**
+
+```yaml filename="interface / replyNotifications"
+interface:
+ replyNotifications:
+ tabBadge: true
+ desktop: true
+ sound: false
+ pollIntervalMs: 60000
+```
+
## defaultPinnedTools
Seeds the initial prompt-bar pinned tools for users who have not customized their pinned tool state. Once a user pins or unpins a tool, LibreChat preserves that user's choice.
@@ -1440,8 +1543,8 @@ Seeds the initial prompt-bar pinned tools for users who have not customized thei
[
'defaultPinnedTools',
'Array of strings',
- 'Tool keys and MCP dropdown/server names that should start pinned in the prompt bar for new or uncustomized users.',
- 'When omitted, built-in tools start unpinned and the MCP dropdown keeps its default pinned state.',
+ 'Built-in tool keys that should start pinned on the composer bar for new or uncustomized users.',
+ 'When omitted, built-in tools start unpinned.',
],
]}
/>
@@ -1449,8 +1552,8 @@ Seeds the initial prompt-bar pinned tools for users who have not customized thei
**Supported values:**
- Built-in tool keys: `artifacts`, `execute_code`, `web_search`, `file_search`, `skills`
-- `mcp` to pin the MCP servers dropdown
-- A specific MCP server name to seed that server as pinned
+
+The `mcp` keyword and MCP server names are still accepted for compatibility, but the redesigned [composer](/docs/features/composer) does not pin MCP servers to the bar. Users turn servers on from the palette's **MCP Servers** section instead.
**Example:**
@@ -1459,7 +1562,66 @@ interface:
defaultPinnedTools:
- artifacts
- execute_code
- - mcp
+```
+
+## composerRecentFiles
+
+_Newer than v0.8.8._ How many recently used files the [composer](/docs/features/composer) palette requests for its **Your files** section before the user searches.
+
+**Key:**
+
+
+
+**Default:** `5`
+
+The palette displays at most five of them, so values above `5` have no visible effect; use a lower value to shorten the list, or `0` to hide it. Searching the palette, or **Show all**, still reaches every file. The value is also capped by the top-level [`fileListLimit`](/docs/configuration/librechat_yaml/object_structure/config#filelistlimit).
+
+```yaml filename="interface / composerRecentFiles"
+interface:
+ composerRecentFiles: 10
+```
+
+## Queue and steer timeouts
+
+_Newer than v0.8.8._ Three timeouts tune how the client handles messages sent while a response is still streaming (queued messages and steers, see [Agents](/docs/features/agents)). The defaults suit most deployments; raise them when a slow proxy or a lagging database replica makes queued messages show as unconfirmed.
+
+
+
+All three must be positive integers.
+
+```yaml filename="interface / queue and steer timeouts"
+interface:
+ queuedTurnReconciliationTimeoutMs: 120000
```
## peoplePicker
diff --git a/content/docs/configuration/librechat_yaml/object_structure/mcp_servers.mdx b/content/docs/configuration/librechat_yaml/object_structure/mcp_servers.mdx
index 927e7ef6c..c1846bb09 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/mcp_servers.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/mcp_servers.mdx
@@ -522,12 +522,7 @@ Enable coordination only after every replica is upgraded and connected to the sa
- **Usage in `headers` and `env`:**
- Once defined under `customUserVars`, these variables can be referenced in the `headers` (for `sse` and `streamable-http` types) or `env` (for `stdio` type) sections using the `{{VARIABLE_NAME}}` syntax.
- Users provide these values through the UI. These settings can be accessed in two ways:
- - **From Assistant Chat Input**: When selecting MCP tools for an assistant, a settings icon will appear next to configurable MCP servers in the tool selection dropdown. Clicking this icon opens a dialog to manage credentials for that server.
-
+ - **From Chat Input**: In the **MCP Servers** section of the [composer's **+** palette](/docs/features/composer#turn-on-tools-skills-and-mcp-servers), configurable servers have a **Configure** control on their row. Choosing it opens a dialog to manage credentials for that server. Selecting a server that still needs credentials opens the same dialog.
", expected one of: librechat, clickhouse`).
+A theme set through a config override (for a role, group or user in the [Admin Panel](/docs/features/admin_panel#configuration-management)) follows the same rules, with one difference: an invalid override theme falls back to the base `interface.theme` from `librechat.yaml`, not to the default theme. Those log lines start with `[getAppConfig]`, for example `[getAppConfig] Ignoring interface.theme from a config override; the base theme applies instead:`.
+
The `colors` and `appearance` maps work differently from the fields around them.
**An unknown token is ignored, and the rest of the theme applies.** A key inside `colors` or `appearance` that this version of LibreChat does not know, whether a typo or a token added in a newer version, costs only itself. The server logs it and leaves unknown colors out of the theme it sends to the browser:
@@ -140,7 +143,7 @@ The browser applies the same rules to the theme it receives and logs `[Deploymen
### Colors
-Colors use the same token names as the theme engine: `rgb-` followed by the token, such as `rgb-surface-primary`, `rgb-text-primary`, `rgb-border-medium`, `rgb-accent-primary` or `rgb-status-error-subtle`. The full list of 106 tokens is `themeColorTokens` in [`packages/data-provider/src/theme.ts`](https://github.com/LibreChat-AI/LibreChat/blob/canary/packages/data-provider/src/theme.ts), which the server and the browser both read, and each token is described in the `IThemeRGB` interface in [`packages/client/src/theme/types/index.ts`](https://github.com/LibreChat-AI/LibreChat/blob/canary/packages/client/src/theme/types/index.ts).
+Colors use the same token names as the theme engine: `rgb-` followed by the token, such as `rgb-surface-primary`, `rgb-text-primary`, `rgb-border-medium`, `rgb-accent-primary` or `rgb-status-error-subtle`. The full list of 130 tokens is `themeColorTokens` in [`packages/data-provider/src/theme.ts`](https://github.com/LibreChat-AI/LibreChat/blob/canary/packages/data-provider/src/theme.ts), which the server and the browser both read, and each token is described in the `IThemeRGB` interface in [`packages/client/src/theme/types/index.ts`](https://github.com/LibreChat-AI/LibreChat/blob/canary/packages/client/src/theme/types/index.ts).
Beyond the surface, text, border, status and syntax palettes, these roles let a theme restyle specific interaction states and components:
@@ -151,7 +154,14 @@ Beyond the surface, text, border, status and syntax palettes, these roles let a
| Inverted and fixed | `rgb-surface-inverted`, `rgb-surface-inverted-hover`, `rgb-text-inverted`, `rgb-surface-fixed`, `rgb-surface-fixed-hover`, `rgb-text-fixed` | Controls drawn in the opposite mode's colors, and controls that keep one color in both modes |
| Disabled | `rgb-surface-disabled`, `rgb-text-disabled`, `rgb-border-disabled` | Disabled controls, when `disabledStyle` is `fill` (see [Appearance](#appearance)) |
| Control border | `rgb-border-control` | The edge of inputs, select triggers and one-time code slots, kept separate from quiet separators because it needs 3:1 contrast |
+| Fields | `rgb-border-field-focus`, `rgb-field-fill`, `rgb-field-text` | A focused field's edge when `fieldFocusStyle` is `border` (follows `rgb-focus-control`), a field's fill when `fieldFillStyle` is `fill` (follows `rgb-surface-primary`), and the typed value (follows `rgb-text-primary`) |
+| Primary button | `rgb-button-primary`, `rgb-button-primary-hover` | The primary Button's fill and hover; follow `rgb-surface-inverted` and `rgb-surface-inverted-hover`, which checkboxes and switches keep using |
+| Inks | `rgb-dialog-title`, `rgb-badge-label` | Dialog titles and badge labels; both follow `rgb-text-primary` |
| Scrim | `rgb-surface-overlay` | The color behind dialogs; its strength is set by the scrim opacities in [Appearance](#appearance) |
+| Media overlay | `rgb-surface-media-overlay`, `rgb-text-on-media` | Scrims, chips and progress drawn over the user's own images (lightbox, image preview, uploads in progress), and the text on them. Bundled themes keep them black and white in both modes |
+| Avatar | `rgb-avatar-fill`, `rgb-avatar-text`, `rgb-avatar-placeholder`, `rgb-avatar-edge` | The default user avatar's fill and glyph (the glyph follows `rgb-text-primary`), the backdrop behind a loading or transparent agent or assistant avatar (follows `rgb-surface-secondary` in light mode and `rgb-surface-tertiary` in dark), and the hairline around the default avatar |
+| File tiles | `rgb-file-document`, `rgb-file-sheet`, `rgb-file-code`, `rgb-file-artifact`, `rgb-file-audio`, `rgb-file-video`, `rgb-file-generic`, `rgb-file-ink` | One fill per kind of file, and the glyph drawn on every tile |
+| Illustration | `rgb-illustration-subtle`, `rgb-illustration`, `rgb-illustration-strong` | The three tones of in-app artwork, such as the file drop zone's illustration |
| Switch | `rgb-switch-unchecked`, `rgb-switch-thumb` | The unchecked switch track, and the knob in both states |
| Table | `rgb-table-header-text`, `rgb-table-header-fill` | Column names, and the opaque fill of a sticky table header |
| Chart series | `rgb-series-1` through `rgb-series-8` | Categorical chart colors, in order |
@@ -162,7 +172,7 @@ A few tokens follow a related token you did set when you leave them out, so a pa
### Appearance
-Appearance values are set **per mode**, and a mode without them uses the defaults below. To change shape in both modes, repeat the values under `light` and `dark`, as in the example above.
+Appearance values are set **per mode**, and a mode without them uses the defaults below. The defaults are the same in both modes except `menuShadow` and `tooltipShadow`, which are heavier in dark mode. To change shape in both modes, repeat the values under `light` and `dark`, as in the example above.
| Key | Controls | Default |
| --- | --- | --- |
@@ -170,6 +180,9 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `roundControlRadius` | Radius of fully rounded controls | `9999px` |
| `surfaceRadius` | Radius of surfaces such as cards and menus | `1rem` |
| `largeSurfaceRadius` | Radius of large surfaces such as dialogs | `1.5rem` |
+| `menuRadius` | Corner radius of menu panels | `0.7rem` |
+| `tooltipRadius` | Corner radius of tooltips | `0.275rem` |
+| `tabRadius` | Corner radius of tab triggers | `0.185rem` |
| `radiusSm` | The `rounded-sm` step used across the app | `calc(0.5rem - 4px)` |
| `radiusMd` | The `rounded-md` step | `calc(0.5rem - 2px)` |
| `radiusLg` | The `rounded-lg` step | `0.5rem` |
@@ -177,6 +190,20 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `radius2xl` | The `rounded-2xl` step | `1rem` |
| `radius3xl` | The `rounded-3xl` step | `1.5rem` |
| `controlHeight` | Height of standard controls | `2.25rem` |
+| `controlPaddingX` | Inline padding of theme-sized controls; follows `spaceNormal` when unset | `0.75rem` |
+| `controlGap` | Gap between a control's icon and label; follows `spaceCompact` when unset | `0.375rem` |
+| `controlFontWeight` | Label weight of theme-sized controls | `500` |
+| `buttonHeight` | Height of the default Button | `2.5rem` |
+| `buttonHeightSm` | Height of the `sm` Button | `2.25rem` |
+| `fieldHeight` | Height of form fields | `2.5rem` |
+| `fieldPaddingY` | Vertical padding of form fields | `0.5rem` |
+| `fieldFocusStyle` | How a focused field shows focus: `ring` draws the focus ring, `border` swaps the field's edge to `rgb-border-field-focus` | `ring` |
+| `fieldFillStyle` | Whether fields stay `transparent` or paint `rgb-field-fill` (`fill`) | `transparent` |
+| `labelSize` | Font size of field labels; follows `textSm` when unset | `0.875rem` |
+| `labelLeading` | Line height of field labels | `1` |
+| `labelFontWeight` | Weight of field labels; `inherit` keeps the weight of the surrounding text | `inherit` |
+| `focusRingWidth` | Width of the keyboard focus outline | `2px` |
+| `focusRingOffset` | Distance of the focus outline from the element's edge | `2px` |
| `switchWidth` | Width of the switch | `2.75rem` |
| `switchHeight` | Height of the switch; the knob is this minus the track's 4px border | `1.5rem` |
| `tableCellSpaceY` | Vertical padding of table cells | `1rem` |
@@ -199,10 +226,18 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `leadingLg` | Line height paired with `text-lg` | `calc(1.75 / 1.125)` |
| `leadingXl` | Line height paired with `text-xl` | `calc(1.75 / 1.25)` |
| `leading2xl` | Line height paired with `text-2xl` | `calc(2 / 1.5)` |
+| `dialogStroke` | Width of the dialog's edge stroke | `0px` |
+| `dialogPaddingX` | Inline padding of dialogs | `1.5rem` |
+| `dialogHeaderGap` | Gap between a dialog's title and description | `0.375rem` |
+| `dialogTitleSize` | Font size of dialog titles; follows `textLg` when unset | `1.125rem` |
+| `dialogTitleLeading` | Line height of dialog titles | `1` |
+| `dialogTitleFontWeight` | Weight of dialog titles | `600` |
+| `dialogTitleFontFamily` | Font family of dialog titles; follows `displayFontFamily` when unset | `Inter, sans-serif` |
| `scrimOpacity` | Strength of the scrim behind standard dialogs | `0.8` |
| `alertScrimOpacity` | Strength of the scrim behind confirmation dialogs | `0.9` |
| `modalScrimOpacity` | Strength of the scrim behind other modal dialogs | `0.65` |
| `elevationSurface` | Shadow of raised theme surfaces | `0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)` |
+| `elevationDrag` | Shadow of a badge while it is dragged | `0 10px 25px rgb(0 0 0 / 0.1)` |
| `shadow2xs` | The `shadow-2xs` step | `0 1px rgb(0 0 0 / 0.05)` |
| `shadowXs` | The `shadow-xs` step | `0 1px 2px 0 rgb(0 0 0 / 0.05)` |
| `shadowSm` | The `shadow-sm` step and bare `shadow` | `0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)` |
@@ -210,6 +245,8 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `shadowLg` | The `shadow-lg` step | `0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)` |
| `shadowXl` | The `shadow-xl` step | `0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)` |
| `shadow2xl` | The `shadow-2xl` step | `0 25px 50px -12px rgb(0 0 0 / 0.25)` |
+| `menuShadow` | Shadow of menu panels; in light mode, follows `shadowLg` when unset | Light: `0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)`; dark: `0 10px 15px -3px rgb(0 0 0 / 0.25), 0 4px 6px -4px rgb(0 0 0 / 0.1)` |
+| `tooltipShadow` | Shadow of tooltips | Light: `0 2px 4px 0 rgb(0 0 0 / 0.25)`; dark: `0 1px 2px 0 rgb(0 0 0 / 0.35)` |
| `motionFast` | Duration of fast transitions | `150ms` |
| `motionNormal` | Duration of normal transitions | `200ms` |
@@ -217,17 +254,24 @@ The defaults reproduce LibreChat's look, so a theme that sets none of these keys
Accepted values:
-- **Radii, `controlHeight`, spacing and text sizes:** `0`, or a number in `px`, `rem` or `em` (such as `0.25rem`), or a single `calc()` of two such lengths (such as `calc(0.5rem - 2px)`).
+- **Radii, `controlHeight`, `controlPaddingX`, `controlGap`, button and field sizes, spacing, text and label sizes, and the dialog lengths (`dialogStroke`, `dialogPaddingX`, `dialogHeaderGap`, `dialogTitleSize`):** `0`, or a number in `px`, `rem` or `em` (such as `0.25rem`), or a single `calc()` of two such lengths (such as `calc(0.5rem - 2px)`).
- **`switchWidth` and `switchHeight`:** a positive length in `px` or `rem`. Both must use the same unit (a side you leave out uses its `rem` default), the width must exceed the height so the knob can travel, and the height must clear the 4px track border (more than `4px`, or at least `0.5rem`).
- **`tableCellSpaceY` and `tableRowStroke`:** `0`, or a length in `px` or `rem`.
+- **`focusRingWidth`:** a positive length in `px` or `rem`, so the focus indicator never disappears.
+- **`focusRingOffset`:** `0`, or a length in `px` or `rem` that may be negative (drawing the outline inside the element's edge).
- **`disabledStyle`:** `dim` or `fill`.
-- **Line heights:** a unitless number (such as `1.5`), a single `calc()` dividing two numbers (such as `calc(1.25 / 0.875)`), or a length.
+- **`fieldFocusStyle`:** `ring` or `border`.
+- **`fieldFillStyle`:** `transparent` or `fill`.
+- **Font weights (`controlFontWeight`, `dialogTitleFontWeight`):** a whole number from `1` to `1000`. `labelFontWeight` also accepts `inherit`.
+- **Line heights (the `leading*` steps, `labelLeading` and `dialogTitleLeading`):** a unitless number (such as `1.5`), a single `calc()` dividing two numbers (such as `calc(1.25 / 0.875)`), or a length.
- **Scrim opacities:** a number from `0` to `1`.
- **Font families:** any non-empty `font-family` list without `;`, `{` or `}`. The font must be available to the browser: LibreChat bundles Inter, Roboto Mono and Inconsolata, so any other family has to be installed on the viewer's machine or served by your deployment, or the next family in the list is used.
-- **Shadow steps (`shadow2xs` through `shadow2xl`):** a concrete `box-shadow` list, or `none`. `var()`, `env()`, `attr()` and `url()` are rejected.
+- **Shadow steps (`shadow2xs` through `shadow2xl`), `menuShadow`, `tooltipShadow` and `elevationDrag`:** a concrete `box-shadow` list, or `none`. `var()`, `env()`, `attr()` and `url()` are rejected.
- **`elevationSurface`:** any non-empty `box-shadow` value without `;`, `{`, `}` or `url()`.
- **Motion:** a duration in `ms` or `s`, such as `120ms`.
+Every appearance value must be a string. Quote numbers such as font weights, line heights and scrim opacities (`'500'`, `'1.5'`, `'0.8'`); an unquoted YAML number is rejected.
+
### Brands
`brands` recolors the provider icons shown next to models. It can be set once at the top level for both modes, and overridden per mode under `modes..brands`.
diff --git a/content/docs/configuration/librechat_yaml/object_structure/web_search.mdx b/content/docs/configuration/librechat_yaml/object_structure/web_search.mdx
index de4156dca..c6114f53c 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/web_search.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/web_search.mdx
@@ -696,11 +696,9 @@ You can configure SearXNG in LibreChat within the UI or through `librechat.yaml`
#### UI Configuration
-1. **Open the tools dropdown in the chat input bar**
-
+1. **Click + in the [message composer](/docs/features/composer) to open the palette**
-2. **Click on the gear icon next to Web Search**
-
+2. **Choose Configure on the Web Search row in the Tools section**
3. **Select SearXNG from the Search Provider dropdown**

@@ -708,11 +706,9 @@ You can configure SearXNG in LibreChat within the UI or through `librechat.yaml`
4. **Enter your configuration details (e.g. instance URL, scraper type, etc.) and click save**

-5. **Click on the Web Search option in the tools dropdown**
-
+5. **Select Web Search in the palette's Tools section**
-6. **The Web Search badge should now be enabled, meaning your queries can now utilize the web search functionality**
-
+6. **A Web Search chip now appears on the composer, meaning your queries can now use web search**
#### YAML Configuration
diff --git a/content/docs/configuration/mod_system.mdx b/content/docs/configuration/mod_system.mdx
index 12b53778d..1900944fa 100644
--- a/content/docs/configuration/mod_system.mdx
+++ b/content/docs/configuration/mod_system.mdx
@@ -59,6 +59,38 @@ The following are all of the related env variables to make use of and configure
]}
/>
+### Two-factor and passkey rate limiting
+
+
+
+The two-factor violation scores are `TWO_FACTOR_TEMP_VIOLATION_SCORE`, which falls back to `LOGIN_VIOLATION_SCORE`, and `TWO_FACTOR_SETUP_VIOLATION_SCORE`, which falls back to `TWO_FACTOR_TEMP_VIOLATION_SCORE` and then `LOGIN_VIOLATION_SCORE`. See [Required Two-Factor Authentication](/docs/configuration/authentication#required-two-factor-authentication) and [Passkeys](/docs/configuration/authentication/passkeys).
+
+### Email change rate limiting
+
+
+
+These can also be set under [`rateLimits.emailChange` and `rateLimits.emailChangeConfirm`](/docs/configuration/librechat_yaml/object_structure/config#ratelimits) in `librechat.yaml`, which take precedence over the environment variables. Exceeding the request limit is scored with `EMAIL_CHANGE_VIOLATION_SCORE` (default `1`). The verification link limits only return HTTP 429: whoever opens the link is not signed in, so there is no account to score.
+
### Message rate limiting
` with your Azure app registration's Application (client) ID
### File Selection Process
-1. User clicks "From SharePoint" in the attachment menu
+1. User selects **From SharePoint** in the **Attach** section of the [composer's **+** palette](/docs/features/composer#add-files)
2. SharePoint Online file picker opens in an embedded iframe
3. User browses and selects files using the familiar SharePoint interface; current selections remain checked while opening other folders or switching picker views
4. Selected files are queued for download
@@ -166,10 +166,10 @@ Replace `` with your Azure app registration's Application (client) ID
### Accessing SharePoint Files
-When properly configured, users will see a new option in the file attachment menu:
+When properly configured, users will see a new option in the **Attach** section of the [composer's **+** palette](/docs/features/composer#add-files):
-1. Click the attachment icon in the message input
-2. Select "From SharePoint" from the menu
+1. Click **+** in the message composer
+2. Select **From SharePoint** (with [`legacyFileUploadUX`](/docs/configuration/librechat_yaml/object_structure/file_config#legacyfileuploadux) enabled, choose a destination labeled with **(From SharePoint)** under **More upload options**)
3. The SharePoint file picker will open
4. Browse and select files as needed
5. Click "Select" to begin downloading
diff --git a/content/docs/features/admin_panel.mdx b/content/docs/features/admin_panel.mdx
index 62fa4f01a..5cbd5ccfa 100644
--- a/content/docs/features/admin_panel.mdx
+++ b/content/docs/features/admin_panel.mdx
@@ -228,6 +228,28 @@ This is the surface behind LibreChat's [DB-backed per-principal configuration ov
Administrators can also use the opt-in [Admin Insights](/docs/features/insights) dashboard to review tenant-scoped MongoDB activity. Set `ENABLE_INSIGHTS=true`, then grant both `access:admin` and `read:insights` to an account with the `ADMIN` role.
+### Override Validation
+
+Override writes (`PUT /api/admin/config/:principalType/:principalId` and `PATCH .../fields`) are validated against the same schema as `librechat.yaml`, applied on top of the deployment's base config. A write that would produce an invalid config is rejected with HTTP 400 and nothing is saved:
+
+```json
+{
+ "error": "Invalid config override",
+ "code": "CONFIG_OVERRIDE_INVALID",
+ "issues": [{ "path": "interface.fileSearch", "code": "invalid_type" }]
+}
+```
+
+Each issue carries only the dot-path and a stable code (a schema issue code such as `invalid_type`, or `missing_merge_key`, `duplicate_merge_key`, `indexed_merge_key_write`, `union_dropped_key` or `invalid_document`), never the submitted value. A field patch is only rejected for problems it touches or introduces, so an older invalid value elsewhere in the same override does not block it.
+
+Stored overrides that have become invalid, for example after an upgrade tightens the schema, are not fatal. When the config is resolved, the invalid fields are dropped, the value beneath them (usually the base `librechat.yaml` value) applies, and the server logs one warning per field:
+
+```text
+[mergeConfigOverrides] Ignoring invalid override "" for / ()
+```
+
+Check the logs for these lines after upgrading, then fix or remove the listed fields in the panel. An override theme that fails the [theme rules](/docs/configuration/librechat_yaml/object_structure/theme#validation) falls back to the base theme in the same way.
+
### Section-Scoped Delegation
Configuration grants can be broad (`read:configs`, `manage:configs`) or limited to one top-level section (`read:configs:`, `manage:configs:`). A section-scoped reader receives a filtered configuration response containing only authorized sections instead of being denied the entire request. Section-level manage grants imply read access to the same section; broad manage access implies broad read access.
diff --git a/content/docs/features/agents.mdx b/content/docs/features/agents.mdx
index 3e7338772..7cb00dea1 100644
--- a/content/docs/features/agents.mdx
+++ b/content/docs/features/agents.mdx
@@ -23,6 +23,7 @@ The creation form includes:
- **Description**: Optional details about your agent's purpose
- **Instructions**: System instructions that define your agent's behavior
- **Model**: Select from available providers and models
+- **Conversation Starters**: Up to four suggested prompts shown when a user starts a new chat with the agent
The **Tools** and **Skills** controls open searchable libraries for built-in capabilities, tools, MCP servers, Actions, and Skills. Select an item to configure it, then save the agent.
@@ -60,6 +61,21 @@ Recognized model-not-found and provider rate-limit failures render localized gui
Users with edit access can open **Version History** from the Agent Builder to inspect saved configurations in a timeline. Each entry shows when it was saved and summarizes its tools and capabilities. Restoring an earlier entry requires confirmation and replaces the current agent configuration with that saved state. Saving always applies the Agent's current changes, even when the resulting configuration matches the newest history entry and LibreChat does not add a duplicate entry.
+### Conversation Starters
+
+
+ Newer than **v0.8.8**. Available now on LibreChat's `dev` and `canary` branches, and in the next release.
+
+
+Conversation starters give users a quick way to begin a chat with an agent. Each agent can store up to four.
+
+1. In the Agent Builder, find the **Conversation Starters** field.
+2. Type a prompt and press **Enter**, or select the **+** button. The input is disabled once four starters are added.
+3. Edit a saved starter in place, or select its **X** button to remove it.
+4. Save the agent.
+
+When a user opens a new chat with the agent, the starters appear as buttons on the empty chat screen. Selecting one sends it immediately as the first message. If the agent has starters, they take precedence over any [`conversation_starters`](/docs/configuration/librechat_yaml/object_structure/model_specs#conversation_starters) defined on the model spec in use. Starters also appear in the agent's [Marketplace](#agent-marketplace) detail dialog.
+
## Agent Capabilities
> **Note:** All capabilities can be toggled via the `librechat.yaml` configuration file. See [docs/configuration/librechat_yaml/object_structure/agents#capabilities](/docs/configuration/librechat_yaml/object_structure/agents#capabilities) for more information.
@@ -212,6 +228,8 @@ Generic Agent and MCP tool cards distinguish **Preparing** (before SDK handoff,
While a tool is running, expanded Bash, Execute Code, and File Authoring detail panes follow streamed commands, code, or preview content to the bottom. Scrolling upward pauses that follow behavior so the current reading position is preserved.
+Bash output renders as terminal-style text. Long output is collapsed to its last 15 lines, where errors usually appear; use **Show more** and **Show less** to expand or collapse it, and the **Copy** button to copy the output. A command that printed nothing shows **No output**. For commands run in an [attached workspace](/docs/features/code_interpreter#attached-environments-and-pairing), a failed command is marked with its reason (**exit code N**, **terminated by** a signal such as `SIGKILL`, or **timed out**), and stderr is styled separately from stdout. A finished command is labelled **Ran command**, and a tool group that contains a single call opens directly to that call's details.
+
### Live Reasoning Labels
Live reasoning labels replace a generic **Thinking** or **Thoughts** heading with a short orientation that evolves as sufficiently long top-level reasoning streams. The label updates the existing reasoning heading in place; it does not add or reorder message parts.
@@ -304,7 +322,7 @@ By default, an agent uses the user's shared personal memory pool. Turn on **Keep
The Artifacts capability enables your agent to generate and display interactive content:
- Create React components, HTML code, and Mermaid diagrams
-- Display content in a separate UI window for clarity and interaction
+- Display content in a dedicated panel beside the chat, which can be expanded to fullscreen or opened in its own browser window
- Configure artifact-specific instructions at the agent level
- [More info about Artifacts](/docs/features/artifacts)
@@ -514,6 +532,24 @@ Conversation surfaces keep the Agent as the visible identity and do not fall bac
For full details on principals, permission bits, and how ACLs compose with role-based feature permissions, see [Access Control](/docs/features/access_control).
+### Agent Marketplace
+
+The **Agent Marketplace** lets users browse the agents available to them. Open it from **Agent Marketplace** in the sidebar; the entry appears only for users whose role is allowed to use the Marketplace.
+
+- **Browse and search**: Agents appear in a grid. Use the search field to find agents by name or description, and the category pills (such as **Top Picks**, **All**, or a specific category) to narrow the list.
+- **Sort**: Use **Sort by** to order agents by **Newest first** (the default), **Oldest first**, **Popular** (most pinned), or **By creator name**.
+- **My agents**: Turn on **My agents** to show only agents you created. Turning it on from **Top Picks** switches to **All**.
+- **Shareable views**: Search, sort, and the **My agents** filter are kept in the page URL (`?q=`, `?sort=`, `?mine=1`), so a filtered view can be bookmarked or shared.
+
+Select an agent card to open its detail dialog, which shows the full description and the agent's contact, along with these actions:
+
+- **Pin** or **Unpin**: Add the agent to, or remove it from, your pinned agents.
+- **Copy Link**: Copy a link that starts a new chat with the agent.
+- **Start Chat**: Open a new conversation with the agent.
+- **Try a conversation starter**: When the agent has [conversation starters](#conversation-starters), select one to open a new chat with that prompt already in the message input. It is not sent automatically, so you can edit it first.
+
+Administrators can control access with the [`interface.marketplace`](/docs/configuration/librechat_yaml/object_structure/interface#marketplace) setting and the role permissions described in [Access Control](/docs/features/access_control).
+
### Administrator Controls
Administrators have access to global permission settings within the agent builder UI:
@@ -547,7 +583,7 @@ Individual users can:
## Steering and Queued Messages
-While an Agent is responding, you can send another message in either of two ways:
+While an Agent is responding, you can send another message from the [composer](/docs/features/composer) in either of two ways:
- **Steer** inserts the message into the current run at its next tool or agent step, so the agent can adjust its work before finishing.
- **Queue** holds the message and sends it as a normal follow-up turn after the current response completes.
@@ -556,19 +592,19 @@ For saved Agent conversations on a current server, ordinary queued follow-ups ar
Server admission is serialized within each conversation queue lane. If predecessor evidence or mixed-version ownership is ambiguous, LibreChat shows **Awaiting reconciliation** and blocks or fails the affected queued turn instead of guessing and starting it out of order.
-**Interrupt & steer** preempts the current provider work. If no answer text or tool activity is safe to keep yet, LibreChat waits through a short grace period, discards the silent or reasoning-only attempt, inserts the message, and restarts the model with that instruction. Once answer text can be kept, LibreChat stops at a provider-safe boundary, preserves the partial response, inserts the message, and resumes the same assistant response. A running tool call is not discarded or interrupted by steering; the message applies when that work reaches a safe boundary.
+**Steer sooner** asks the agent to take the message at the next safe point instead of waiting for the next tool step. If no answer text or tool activity is safe to keep yet, LibreChat waits through a short grace period, discards the silent or reasoning-only attempt, inserts the message, and restarts the model with that instruction. Once answer text can be kept, LibreChat stops at a provider-safe boundary, preserves the partial response, inserts the message, and resumes the same assistant response. A running tool call is not discarded or interrupted by steering; the message applies when that work reaches a safe boundary.
Use **Stop** to end active reasoning and request cancellation of a foreground tool call. Stop forwards cancellation to signal-aware foreground tools; detached background work keeps its independent lifecycle. Use the dedicated composer control, choose it from the send-button menu, or press `Command/Ctrl + Shift + .`. The action falls back to ordinary steering when the deployment cannot interrupt the active provider stream.
-Under **Settings → Chat**, **While generating, Enter will** chooses the default action. The send-button menu can override that choice for an individual message, and **Steering interrupts generation** controls whether ordinary steering also requests an interrupt.
+Under **Settings → Chat**, **While generating, Enter will** chooses the default action. The send-button menu can override that choice for an individual message, and **Steer sooner on Enter** makes Enter request that earlier safe point when steering is the default.
Files and [quoted excerpts](/docs/features/message_actions#quote-excerpts) travel with steer, queue, and interrupt messages. Manually selected Skills remain staged for the next full turn instead of being attached to a mid-run steer.
-Pending steers appear above the composer until the server inserts them into the run. Their receipt progresses from **Sending** to **Delivered**, then **Interrupting** when a preempt is armed, and finally **Applied** with a double checkmark at the inline message's bottom-right edge. Confirmed applied receipts remain visible after reload and in share or search views; uncertain or failed delivery never shows a confirming checkmark. Once acknowledged, the message menu can reclaim the steer for editing, convert it into a queued follow-up, or cancel it and restore its text and attachments to the composer. LibreChat avoids overwriting a newer draft; if the composer is no longer available, it preserves the reclaimed message in the queue instead. A failed steer remains available to retry, edit, queue, or remove.
+Pending steers appear at the end of the streaming reply, where they will land, until the server inserts them into the run. Their receipt progresses from **Sending** to **Delivered**, then **Steer sooner requested** when an earlier safe point was requested, and finally **Applied** with a double checkmark at the inline message's bottom-right edge. Confirmed applied receipts remain visible after reload and in share or search views; uncertain or failed delivery never shows a confirming checkmark. Once acknowledged, the message menu can reclaim the steer for editing, convert it into a queued follow-up, or cancel it and restore its text and attachments to the composer. LibreChat avoids overwriting a newer draft; if the composer is no longer available, it preserves the reclaimed message in the queue instead. A failed steer remains available to retry, edit, queue, or remove.
-Queued follow-ups can be sent immediately, converted into a steer while the run is still active, escalated to **Interrupt & steer now**, edited, or removed. In-flight steers offer the same escalation when they are still waiting for a tool boundary. Removing a server-backed queued message first cancels its durable source, then restores it to an empty composer when possible. If the preceding response is aborted or fails, LibreChat marks the affected queued turn as failed for review instead of silently sending it.
+Queued follow-ups appear in a list above the composer. Each one can be edited (**Edit message**), removed (**Remove message**), sent right away with **Send now**, or reordered by dragging its handle or pressing the arrow keys on it. Queued follow-ups can also be converted into a steer while the run is still active or escalated with **Steer sooner**. In-flight steers offer the same escalation when they are still waiting for a tool boundary. Removing a server-backed queued message first cancels its durable source, then restores it to an empty composer when possible. If the preceding response is aborted or fails, LibreChat marks the affected queued turn as failed for review instead of silently sending it.
-A conversation can have up to 100 active queued turns. Each queued turn accepts up to 16,000 characters and 10 files. Separately, one active run accepts up to 10 pending steers; each steer can include up to 10 files and is limited by [`STEER_MAX_LENGTH`](/docs/configuration/dotenv#agent-conversation-controls). **Interrupt & steer** continues to use the live steering path rather than moving a queued turn to the front of the durable FIFO lane.
+A conversation can have up to 100 active queued turns. Each queued turn accepts up to 16,000 characters and 10 files. Separately, one active run accepts up to 10 pending steers; each steer can include up to 10 files and is limited by [`STEER_MAX_LENGTH`](/docs/configuration/dotenv#agent-conversation-controls). **Steer sooner** continues to use the live steering path rather than moving a queued turn to the front of the durable FIFO lane.
Redis-backed multi-replica deployments negotiate generation protocol v2 automatically. Follow the [generation protocol compatibility guidance](/docs/configuration/redis#generation-protocol-compatibility) when upgrading from a release older than `v0.8.8-rc1`.
diff --git a/content/docs/features/artifacts.mdx b/content/docs/features/artifacts.mdx
index 36591f5b4..f4f9ff89c 100644
--- a/content/docs/features/artifacts.mdx
+++ b/content/docs/features/artifacts.mdx
@@ -37,6 +37,14 @@ Agent-level configuration is preferred because each agent can use the mode and i
Use the fullscreen control in a rendered artifact preview to expand it to the complete browser display. The control follows browser Fullscreen API state, exits normally with Escape or browser controls, and is hidden when fullscreen is unavailable.
+On desktop, select **Open in new window** (newer than **v0.8.8**) in the artifact panel header to move the panel into a separate browser window, for example to place it on a second screen. The artifact keeps updating as the response streams, and unsaved code edits and the selected tab carry over. To return the panel to the chat, select **Dock back to panel** or close the window. LibreChat remembers the window's size and position for the next time.
+
+- Keep the chat tab open: the window is controlled by the chat tab, and closing that tab also closes the window.
+- If the browser blocks the window, LibreChat shows a notice. Allow pop-ups for the site and try again.
+- The button is not shown on narrow (mobile) layouts.
+
+Administrators can remove the button and keep artifacts in the side panel by setting [`interface.artifactUndocking: false`](/docs/configuration/librechat_yaml/object_structure/interface#artifactundocking).
+
Mermaid diagrams appear as compact inline cards that can open in the artifact panel. Export a diagram as SVG or PNG from either the inline card or the panel. Mermaid previews render directly rather than loading the Sandpack bundler; PNG export applies bounded canvas dimensions to protect the browser from oversized diagrams.
Model-authored artifacts download under their title or Markdown heading with an extension matching the exported content. An unedited file-backed artifact downloads the complete original with its original filename and format when available. Downloading edited or cached preview content uses a `.preview` qualifier so it is not mistaken for the original file.
diff --git a/content/docs/features/authentication.mdx b/content/docs/features/authentication.mdx
index f5467e5ff..1fa5edca2 100644
--- a/content/docs/features/authentication.mdx
+++ b/content/docs/features/authentication.mdx
@@ -20,6 +20,27 @@ Additionally, our system can integrate social logins from various platforms such
**See also:** [Access Control](/docs/features/access_control), LibreChat's granular permission system for users, groups, and roles, covering per-resource sharing of agents, prompts, MCP servers, and feature-level permissions.
+## Two-Factor Authentication
+
+Local and LDAP accounts can add a second factor (a one-time code from an authenticator app, plus backup codes) under **Settings > Account > Two-factor authentication**. Accounts that sign in through an identity provider (OAuth, OpenID Connect, SAML) use that provider's MFA instead.
+
+Administrators can make 2FA mandatory with `ENFORCE_TWO_FACTOR_AUTHENTICATION=true`. Users without 2FA then see a **Two-Factor Authentication Required** screen after signing in and must finish setup before they can use LibreChat. Once enforced, the disable control shows **Required by administrator** and 2FA cannot be turned off. See [Required Two-Factor Authentication](/docs/configuration/authentication#required-two-factor-authentication).
+
+## Passkeys
+
+When an administrator enables them, local-account users can sign in with **Sign in with a passkey** instead of a password, using their device screen lock, a password manager, or a security key. Passkeys are added and removed under **Settings > Account > Passkeys**; both actions ask for the account password first. If the account also has 2FA, the code is still requested after a passkey sign-in. See [Passkeys](/docs/configuration/authentication/passkeys) for setup.
+
+## Changing Your Email Address
+
+Local-account users can change their registered email under **Settings > Account > Email address** by selecting **Change**. In the **Change email address** dialog, enter the new address and the current password, then select **Send verification link**.
+
+- The change takes effect only after the link sent to the **new** address is opened. The link is single-use and expires after 15 minutes by default.
+- The **current** address receives a security notice as soon as a change is requested. After the change is confirmed, both addresses receive an "email changed" notice.
+- The new address must not belong to another account and must be in the deployment's allowed registration domains, if any are set.
+- Changing your password before opening the link makes the link invalid; request a new one.
+
+The option appears only when the server can send email and the feature is enabled. See [Email address change](/docs/configuration/authentication/email#email-address-change).
+
## 2FA Management Attempt Limits
Two-factor authentication settings share an account-level attempt budget: **7 requests per 5 minutes** by default, across enabling, verifying, confirming, disabling, and regenerating backup codes. Successful requests consume attempts too. Opening a new session does not create another budget for the same tenant and account.
diff --git a/content/docs/features/code_interpreter.mdx b/content/docs/features/code_interpreter.mdx
index 3d6b21f04..ac55ec18b 100644
--- a/content/docs/features/code_interpreter.mdx
+++ b/content/docs/features/code_interpreter.mdx
@@ -123,6 +123,8 @@ Personal environments are bound to the authenticated user and tenant, protected
Agents using an attached workspace can list its directory tree, read files, search file contents, create or edit files, and run Bash on the selected worker. File citations use workspace-relative paths, authored files appear in the response's **Workspace changes** row, and tool failures preserve the worker's bounded HTTP diagnostics. An Agent can also store a per-Agent Git name and email for commits created in that workspace; these values configure authorship only and do not provide repository credentials.
+In the chat, a failed attached-workspace Bash command shows its exit code, terminating signal, or timeout, and its stderr is styled separately from stdout. See [Activity Groups](/docs/features/agents#activity-groups) for how Bash output is displayed.
+
An attached worker advertises the workspace roots it makes available. The composer lets the user select one workspace for each attached environment reachable through the Agent or its Subagents; a single unambiguous workspace is selected automatically for a new conversation. LibreChat stores these selections on the conversation, revalidates them against the live worker before execution, and keeps them fixed through approval pauses and resumed runs. Sending is blocked when a required selection is missing or unavailable, and LibreChat never silently substitutes another workspace. At the start of a run, LibreChat can load repository instructions from the selected workspace so the Agent follows that repository's guidance; administrators can bound discovery with [`repositoryInstructions.timeoutMs`](/docs/configuration/librechat_yaml/object_structure/agents#repositoryinstructions). Native file and Bash workspace tools can use an advertised root even when the worker does not support reusable runtime sessions. Workspace-aware Programmatic Bash is available on compatible Code API and worker builds when the Agent's programmatic-tool configuration permits it, the authorized attached worker is ready and advertises `programmaticLanguages: ['bash']`, and both the selected workspace and worker allow `execute_command`. LibreChat passes the server-validated workspace ID and conversation workspace-instance ID, when present, to the programmatic tool; model arguments cannot replace that selection. Without these capabilities, Programmatic Bash is disabled; use direct Bash for workspace-aware commands. When the worker supports file relay, chat uploads are staged separately under `$LIBRECHAT_CODE_DATA_DIR` for the programmatic run, not copied into the selected workspace. **Stop** cancels a signal-aware in-flight BYOM command without invalidating the workspace for later commands; detached work retains its separate cancellation lifecycle.
For an attached execution environment, the Agent Builder can set a **Workspace default** to one currently advertised root. It is validated against that attached environment and used to initialize new conversations for that Agent. Choose **Last used** to use the signed-in user's browser-local preference for that Agent and environment; it is only a convenience hint, never an authorization grant or conversation binding. Changing the execution environment clears an explicit default, and a saved root that is no longer advertised remains visible but cannot be selected until it is reconfigured.
diff --git a/content/docs/features/composer.mdx b/content/docs/features/composer.mdx
new file mode 100644
index 000000000..fc64c420a
--- /dev/null
+++ b/content/docs/features/composer.mdx
@@ -0,0 +1,143 @@
+---
+title: Message Composer
+icon: MessageSquare
+description: Write messages, attach files, and turn on tools, skills and MCP servers from the composer's + palette, then queue or steer follow-ups while a response streams.
+---
+
+The message composer is the box at the bottom of every chat. Besides the text field, it holds one **+** button that opens everything you can add to a message (files, tools, skills, MCP servers and files you uploaded before), the chips for the tools that are currently on, and the controls for reasoning, dictation and sending.
+
+
+ The composer described on this page is newer than **v0.8.8**. Earlier releases use a toolbar with separate attach and MCP Servers dropdowns. The redesign is available on LibreChat's `dev` and `canary` branches and ships in the next release.
+
+
+## What's on the composer
+
+| Element | What it does |
+|---|---|
+| **+** (**Attach and tools**) | Opens the palette: upload options, tools, skills, MCP servers and your recent files, all searchable from one field. |
+| Tool chips | One chip per tool or MCP server that is on, next to the **+** button. Remove a chip to turn that tool off. |
+| Staged context | Files, quotes and skills attached to your next message, shown above the text field. |
+| **Thinking** | Reasoning effort for models that support it. |
+| Context usage | How much of the model's context window the conversation uses, when token usage is available. |
+| **Use microphone** | Dictates your message with speech to text, when speech to text is enabled. |
+| Send / Stop | Sends the message, or stops the response that is streaming. |
+
+To see which keys apply right now, such as `/ for prompts`, `@ for models` or `Enter to send`, turn on **Show composer tips** in **Settings > General**. A one-line tip then appears under the text field. It is off by default.
+
+
+
+## Add files
+
+Click **+** and choose an option in the **Attach** section. You can also drag files onto the composer or paste them.
+
+By default, LibreChat uses the unified uploader: you pick only a source, and it decides how each file reaches the model (native provider upload, text extraction, or a tool such as File Search) from the file type and the endpoint.
+
+| Option | When it appears |
+|---|---|
+| **From Local Computer** | Always, with the default uploader. |
+| **From SharePoint** | When the [SharePoint file picker](/docs/configuration/sharepoint) is enabled. |
+
+If an administrator sets [`fileConfig.legacyFileUploadUX: true`](/docs/configuration/librechat_yaml/object_structure/file_config#legacyfileuploadux), you choose the destination yourself instead. The **Attach** section then shows the main option first and folds the rest behind **More upload options**:
+
+| Option | When it appears |
+|---|---|
+| **Upload to Provider** | The provider accepts documents directly (for example OpenAI, Anthropic, Google, Bedrock, OpenRouter and custom endpoints, or Azure OpenAI with the Responses API on). |
+| **Upload Image** | Shown instead of **Upload to Provider** when the provider only accepts images. |
+| **Upload as Text** | The [context capability](/docs/features/upload_as_text) is enabled. |
+| **Upload for File Search** | File Search is enabled and allowed for the current agent. Picking it also turns File Search on. |
+| **Upload to Code Environment** | Code execution is enabled and allowed for the current agent. Picking it also turns **Run Code** on. |
+| **Attach Files** | Assistants endpoints, which handle files with the assistant's own configuration. |
+
+With SharePoint enabled in this mode, each option also has a SharePoint version, labeled for example **Upload to Provider (From SharePoint)**.
+
+### Reuse a file you uploaded before
+
+The **Your files** section of the palette lists your most recently used files. Select one to attach it again without uploading it. Typing in the search field searches all of your files by name.
+
+Choose **Show all** on the section to browse every file in a dialog, with **All**, **Images** and **Documents** views, a search field, and a preview for images and PDFs.
+
+The palette shows up to five recent files. Administrators can lower that number, or hide the list with `0`, using [`interface.composerRecentFiles`](/docs/configuration/librechat_yaml/object_structure/interface#composerrecentfiles).
+
+## Turn on tools, skills and MCP servers
+
+The palette lists everything you can turn on for the current conversation, grouped into sections:
+
+- **Tools**: built-in tools such as **Web Search**, **Run Code**, **File Search**, **Skills**, **Memory** and **Artifacts**, each shown only when it is enabled and you have access to it.
+- **Skills**: individual [skills](/docs/features/skills) you can attach to your next message.
+- **MCP Servers**: the [MCP servers](/docs/features/mcp) available in chat.
+
+Select a tool or MCP server to switch it on or off. The palette stays open, so you can switch several in one visit. Each tool that is on appears as a chip on the composer; remove the chip to switch it off. Some chips carry a mode menu, for example the **Artifacts** generation mode.
+
+Selecting a skill stages it for your next message instead: it appears in the [staged context](#staged-context) rather than as a chip.
+
+MCP server rows show each server's connection status. Selecting a server that is not connected starts its connection (including an OAuth sign-in, when the server needs one) or opens its configuration first if it needs your credentials. Once connected, servers that take user credentials also have a **Configure** control on the row, and **Web Search** has one for its API keys.
+
+
+ With an agent selected, the **Tools** and **MCP Servers** sections are hidden, because the agent's own configuration decides which tools it uses. On other endpoints, a [model spec](/docs/configuration/librechat_yaml/object_structure/model_specs#hidebadgerow) with `hideBadgeRow: true` hides the **Tools**, **Skills** and **MCP Servers** sections. **Attach** and **Your files** stay available in every case.
+
+
+### Show all
+
+The palette shows a handful of rows per section. Choose **Show all** on the **Skills**, **MCP Servers** or **Your files** header to open a dialog with the full list, a search field and filters. Skills and MCP servers have three views: **All**, **Made by you** and **Favorites**.
+
+### Favorites and pinned tools
+
+Star a row to add it to **Favorites**, which sits at the top of the palette. With a row highlighted, press Ctrl+D (Cmd+D on macOS) to star or unstar it. Favorites are saved to your account.
+
+Administrators can pin built-in tools with [`interface.defaultPinnedTools`](/docs/configuration/librechat_yaml/object_structure/interface#defaultpinnedtools). A pinned tool keeps its chip on the composer even while it is off, so you can switch it on with one click. Removing the chip of a pinned tool that is off unpins it.
+
+## Staged context
+
+Everything attached to your next message appears above the text field before you send it:
+
+- **Files**, with upload progress and a preview for images. Long pasted text can also land here as a file, which you can edit or move back into the message.
+- **Quotes** you selected from earlier messages.
+- **Skills** you picked from the palette or with the `$` command.
+
+Remove any item with its remove button. Everything staged is sent with your next message.
+
+## Thinking and effort
+
+For models with a reasoning setting, the **Thinking** control opens a slider that runs from **Faster** to **Smarter**, plus the provider's separate modes such as **Auto** or off. It changes the same parameter as the Parameters panel, so it is hidden when the model has no reasoning setting or when the deployment turns parameters off with `interface.parameters`.
+
+## Queue and steer while a response streams
+
+You can keep typing while a response is streaming. Depending on the endpoint and your settings, pressing Enter either queues the message to send after the response, or steers an agent's current response. The composer tip names the action that Enter performs at that moment.
+
+Queued messages appear in a **Queued messages** rail above the composer, where you can:
+
+- **Edit message** to move it back into the text field.
+- **Remove message** to take it out of the queue.
+- **Send now** to send it right away instead of waiting.
+- Reorder messages by dragging them, or by focusing one and pressing the up and down arrow keys.
+
+For how steering works and when it is available, see [Steering and Queued Messages](/docs/features/agents#steering-and-queued-messages).
+
+## Keyboard shortcuts
+
+### In the text field
+
+| Keys | Action |
+|---|---|
+| Enter | Send the message (queue or steer while a response streams). |
+| Shift+Enter | New line. |
+| Ctrl+Enter (Cmd+Enter) | While a response streams: the alternate action, **send now** when Enter queues, or **queue** when Enter steers. |
+| Alt+Enter (Option+Enter) | While an agent responds: interrupt and send, when available. |
+| Ctrl+Shift+X (Cmd+Shift+X) | Stop the response. |
+| `/` | Insert a saved prompt. |
+| `@` | Switch to another model, preset or agent. |
+| `+` | Add a model or preset for an additional response. |
+| `$` | Pick a skill for the next message. |
+
+If you turned off **Press Enter to send messages**, Ctrl+Enter (Cmd+Enter) sends and Enter adds a new line.
+
+### In the palette
+
+| Keys | Action |
+|---|---|
+| Type | Search tools, skills, servers, upload options and files. |
+| ↑ / ↓ | Move through the rows. |
+| Enter | Select the highlighted row. |
+| Ctrl+D (Cmd+D) | Add the highlighted row to Favorites, or remove it. |
+| Backspace on an empty search | Close the palette. |
+| Escape | Close the palette. |
diff --git a/content/docs/features/mcp.mdx b/content/docs/features/mcp.mdx
index 90c8019e4..3aa008a13 100644
--- a/content/docs/features/mcp.mdx
+++ b/content/docs/features/mcp.mdx
@@ -37,12 +37,12 @@ Register this exact callback URL with the OAuth provider. Local Docker installs
### In Chat Area
-
+
LibreChat displays configured MCP servers directly in the chat area when using traditional endpoints (OpenAI, Anthropic, Google, Bedrock, etc.):
- Select any non-agent endpoint first, and a tool-compatible model
-- MCP servers appear in a dropdown in the chat interface below your text input
+- MCP servers appear in the **MCP Servers** section of the [composer's **+** palette](/docs/features/composer#turn-on-tools-skills-and-mcp-servers), and each selected server shows as a chip on the composer
- When selected, all tools from that server become available to your current model
- Quick access to MCP tools without creating an agent, allowing multiple servers to be used at once
@@ -131,7 +131,7 @@ Your new server will appear in the MCP Settings panel with a confirmation toast.
#### Step 3: Check Connection Status and Authenticate
-Review the [connection status indicator](#connection-status-indicators) for your new server. If the server requires OAuth authentication, the status will show as disconnected. Click the server's authenticate/connect button (you can do this either by clicking on the MCP server itself in the chat dropdown menu, or by clicking on the connection icon first to be taken to a dialog with more information on the connection state) to begin the authentication flow.
+Review the [connection status indicator](#connection-status-indicators) for your new server. If the server requires OAuth authentication, the status will show as disconnected. Select the MCP server in the composer's **+** palette to begin the authentication flow.

@@ -155,7 +155,7 @@ After authenticating, you'll see a success confirmation. This window will automa
#### Step 6: Server Ready for Use
-LibreChat acknowledges the successful authentication and automatically selects the MCP server for use within your conversation. The server now shows a connected status indicator and is checked in the MCP Servers dropdown.
+LibreChat acknowledges the successful authentication and automatically selects the MCP server for use within your conversation. The server now shows a connected status indicator and appears as a selected chip on the composer.

@@ -226,7 +226,7 @@ LibreChat provides comprehensive tools for managing MCP server connections with
### Connection Status Indicators
-LibreChat displays dynamic status icons showing the current state of each MCP server in the chat dropdown and settings panel:
+LibreChat displays dynamic status icons showing the current state of each MCP server in the composer's **+** palette and the settings panel:

@@ -249,13 +249,13 @@ You can initialize or re-initialize MCP servers directly from the interface:
**One click:**
-- One-click initialization from the MCP server selection dropdown
+- One-click initialization by selecting the server in the **MCP Servers** section of the composer's **+** palette
**From MCPConfigDialog:**
-- Click the status icon next to an MCP server in the Chat Dropdown to open the MCPConfigDialog
+- Choose **Configure** on an MCP server's row in the composer's **+** palette to open the MCPConfigDialog
- Configure custom user variables and click the Authenticate/Initialize button depending on the server authentication type
@@ -395,7 +395,7 @@ mcpServers:
Users can configure these credentials:
-- **From Chat Area**: Click the settings icon next to configurable MCP servers in the tool selection dropdown
+- **From Chat Area**: Choose **Configure** on a configurable MCP server's row in the composer's **+** palette
- **From MCP Settings Panel**: Access "MCP Settings" in the right panel to manage credentials for all configured servers
#### Reinitializing MCP Servers with User Credentials
@@ -465,8 +465,8 @@ mcpServers:
When you first configure an OAuth-enabled MCP server:
1. **Initial Connection**: LibreChat attempts to connect to the MCP server
-2. **Authentication Required**: If no valid token exists, you'll see an OAuth authentication indicator in the chat dropdown for that server
-3. **Button Interface**: Click the authentication indicator to open the MCP configuration dialog and begin the OAuth flow
+2. **Authentication Required**: If no valid token exists, you'll see an OAuth authentication status for that server in the composer's **+** palette
+3. **Start Sign-In**: Select the server to begin the OAuth flow
4. **Continue or use another device**: Continue in the current browser, copy the authorization link, or reveal a QR code to open it on another device
5. **Browser Redirect**: Complete authentication with the OAuth provider
6. **Return Handling**: LibreChat automatically processes the OAuth callback once you've authenticated
diff --git a/content/docs/features/memory.mdx b/content/docs/features/memory.mdx
index 0644f0c0d..06dbc182d 100644
--- a/content/docs/features/memory.mdx
+++ b/content/docs/features/memory.mdx
@@ -108,7 +108,7 @@ The `maxInputTokens` parameter caps the recent-chat text sent to the automatic m
When `personalize` is set to `true`:
-- Users see a memory toggle in their chat interface
+- Users see a **Memory** toggle in the **Tools** section of the [composer's **+** palette](/docs/features/composer#turn-on-tools-skills-and-mcp-servers)
- They can enable/disable memory for individual conversations
- Memory settings persist across sessions
diff --git a/content/docs/features/meta.json b/content/docs/features/meta.json
index 41008f9bb..7f98e9c0e 100644
--- a/content/docs/features/meta.json
+++ b/content/docs/features/meta.json
@@ -22,6 +22,7 @@
"ocr",
"image_gen",
"---Chat---",
+ "composer",
"resumable_streams",
"smooth_streaming",
"settings",
diff --git a/content/docs/features/navigation.mdx b/content/docs/features/navigation.mdx
index 19caf2d7b..a2f568116 100644
--- a/content/docs/features/navigation.mdx
+++ b/content/docs/features/navigation.mdx
@@ -12,9 +12,45 @@ Use the sidebar toggle to open or collapse the navigation. Select a panel from t
The unified **Pinned** section interleaves pinned conversations with favorite models, Agents, and presets. On pointer-based desktops, drag items to reorder them; drag a conversation onto **Pinned** to pin it, onto a Project to file it there, or onto **Chats** to remove its Project assignment. Pinned conversations load independently from regular history, and pinning, unpinning, or reordering does not count as new conversation activity. Touch uses the existing menus instead of drag interactions.
+## Filter and Sort the Chat List
+
+
+ Newer than **v0.8.8**. Available now on LibreChat's `dev` and `canary` branches, and in the next release.
+
+
+The **Filter and sort chats** button on the **Chats** heading opens the chat list menu. It has three rows, each showing its current value:
+
+- **Show** switches between **Active chats** and **Archived chats**.
+- **Sort** orders the list by **Updated**, **Created**, or **Title**, plus **Date Archived** in the archived view. Under **Order**, choose **Newest first** or **Oldest first** for dates, and **A to Z** or **Z to A** for titles.
+- **Filter** narrows the list. Its **Search filters** box finds a filter or value by name.
+
+The **Filter** submenu offers:
+
+| Filter | Choices |
+| --- | --- |
+| **Updated**, **Created** | **Any time**, **Today**, **Previous 7 days**, **Previous 30 days**, **Previous year** |
+| **Endpoint** | One or more of the endpoints the deployment serves |
+| **Has attachments** | Only chats with files |
+| **Shared only** | Only chats with an active shared link; shown when shared links are enabled |
+| **Bookmarks** | One or more bookmarks; shown to roles that can use bookmarks |
+
+When anything differs from the default list, the button shows how many settings are active. **Reset** in the menu header restores the whole list, and **Clear filters** at the bottom of the **Filter** submenu clears only the filters. Show and Filter choices last until you reload or sign out; the sort order is remembered in the current browser. Administrators can cap how many endpoints one filter may select with [`conversationList`](/docs/configuration/librechat_yaml/object_structure/config#conversationlist).
+
+## Unread Replies
+
+
+ Newer than **v0.8.8**. Available now on LibreChat's `dev` and `canary` branches, and in the next release.
+
+
+When a reply finishes while you are in another chat or away from LibreChat, its conversation shows an unread dot in the chat list until you open it and the reply is on screen. Read state is saved to your account, so opening a chat on one device clears the dot on your others.
+
+To come back to a chat later, open its menu and choose **Mark as unread**. The option is not offered for the chat you have open or for a chat that is already unread.
+
+Temporary chats never show as unread, and neither does a reply that has nothing to read, such as one stopped before its first token. For tab-title counts, desktop notifications, and sounds, see [Notifications](/docs/features/settings#notifications).
+
## Mobile Drawer
-On mobile, the sidebar opens as a full-screen drawer by default. Its header contains the same sidebar toggle used in chat, the current panel name, a labeled panel switcher, and account controls. Search appears in the bottom bar while the conversation-history panel is active, beside a thumb-accessible **New chat** action.
+On mobile, the sidebar opens as a full-screen drawer by default. Its header holds the sidebar toggle, a labeled panel switcher, a **New chat** icon button, and account controls, and stays the same on every panel. Users with access to the Agent Marketplace also see an **Agent Marketplace** row above the panel content. The bottom bar holds conversation search only, and only while the chat history panel is active.
The browser-only **Settings > General > Layout > Show chat beside the sidebar on mobile** preference is off by default. Turning it on limits the drawer to 80% of the viewport, leaving a dimmed strip of the current conversation visible. Tap the strip to close the drawer and return to the conversation.
@@ -27,7 +63,7 @@ The drawer follows the gesture and settles open or closed based on distance and
## Archived Conversations
-Open **Archived chats** from **Settings > General** to browse or restore archived conversations. Newly archived chats are ordered by when they were archived. Conversations archived before LibreChat recorded that timestamp fall back to their creation date.
+Open **Archived chats** from the account menu to browse or restore archived conversations, or switch the sidebar list to archived chats with **Show** in the [chat list menu](#filter-and-sort-the-chat-list). Newly archived chats are ordered by when they were archived. Conversations archived before LibreChat recorded that timestamp fall back to their creation date.
To clear the active chat list without deleting its conversations, use **Archive all chats** under **Settings > Data Controls > Your data** and confirm the action.
diff --git a/content/docs/features/ocr.mdx b/content/docs/features/ocr.mdx
index 98754b99a..5c9fcf444 100644
--- a/content/docs/features/ocr.mdx
+++ b/content/docs/features/ocr.mdx
@@ -172,14 +172,14 @@ Support for custom OCR providers and user-defined strategies is planned for futu
### 5. Upload Files to Provider (Direct)
For supported LLM Providers (**OpenAI, AzureOpenAI, Anthropic, Google, and AWS Bedrock**) and their respective models, files can now be sent directly to the provider APIs as message attachments,
-allowing the provider to use their own native OCR implementations to parse files using the `Upload to Provider` option in the file attachment dropdown menu.
+allowing the provider to use their own native OCR implementations to parse files with the default uploader, or with the **Upload to Provider** option in the [composer's **+** palette](/docs/features/composer#add-files) when `legacyFileUploadUX` is enabled.
Currently all five of the aforementioned providers offer support for images and PDFs, with Google also including support for audio and video files when used in conjunction with compatible multimodal models. AWS Bedrock additionally supports CSV, DOC, DOCX, XLS, XLSX, HTML, TXT, and Markdown documents.
For **Azure OpenAI** endpoints, the Upload to Provider option for PDF files is only available when using the Responses API. Azure OpenAI's Chat Completions API supports images but does not support PDF file attachments.
-If you do not see 'Upload to Provider' as an option for PDFs in your chat's attachment dropdown menu with Azure OpenAI, ensure that the Responses API parameter is enabled in the Parameters panel.
+If PDFs are not sent to the provider with Azure OpenAI (or you do not see **Upload to Provider** for them with `legacyFileUploadUX` enabled), ensure that the Responses API parameter is enabled in the Parameters panel.
Note: Standard OpenAI endpoints support PDF uploads in both Chat Completions and Responses APIs.
diff --git a/content/docs/features/projects.mdx b/content/docs/features/projects.mdx
index f00fe5ae1..c210462cd 100644
--- a/content/docs/features/projects.mdx
+++ b/content/docs/features/projects.mdx
@@ -1,30 +1,30 @@
---
title: Projects
icon: FolderKanban
-description: Organize related chats into personal project workspaces.
+description: Organize related chats into personal project workspaces with shared instructions and files.
---
-Projects let each user organize related conversations into named workspaces. They are useful for long-running workstreams, teams, clients, classes, or any topic where you want a focused set of chats without relying on search alone.
+Projects let each user organize related conversations into named workspaces. They are useful for long-running workstreams, teams, clients, classes, or any topic where you want a focused set of chats that share the same background without repeating it in every conversation.
- Projects were added in LibreChat **v0.8.7**. There is no `librechat.yaml` setting, environment
- variable, or role permission that enables or hides them, so every signed-in user on a supported
- version has the feature. If you cannot find Projects anywhere in the interface, the instance is
- running an older version and needs to be updated.
+ Projects were added in LibreChat **v0.8.7**. Project instructions and files are newer than
+ **v0.8.8** and need a build that includes them. There is no switch or role permission that hides Projects, so every signed-in
+ user on a supported version has the feature. Administrators can tune the instruction,
+ description, and file limits with the [`projects`](/docs/configuration/librechat_yaml/object_structure/config#projects)
+ block in `librechat.yaml`. If you cannot find Projects anywhere in the interface, the instance
+ is running an older version and needs to be updated.
## What Projects Do
- Group conversations in a named workspace with an optional description
-- Start a new chat already scoped to a project
-- Move an existing conversation between projects or remove its project assignment
+- Give every chat in the project the same **Instructions**
+- Attach reference **Files** that chats in the project can search
+- Start a new chat already scoped to a project, or move existing chats in and out
- Browse, sort, move, remove, or delete chats from the project workspace
-- Search projects by name or description and sort by latest activity, creation date, or name
-- Edit or delete projects from the dashboard, workspace, or sidebar
+- Search projects by name or description and sort them by latest activity, creation date, or name
-Projects are personal to the user who creates them. Other users do not see your project list, and a project cannot be shared or transferred.
-
-A project is a grouping only. It carries a name and an optional description, and it does not add project-level instructions, files, or model settings to the chats inside it.
+Projects are personal to the user who creates them. Other users do not see your project list, and a project cannot be shared or transferred. Projects do not carry model settings: each chat keeps its own model, Agent, and parameters.
## Create a Project
@@ -46,15 +46,63 @@ A project is a grouping only. It carries a name and an optional description, and
-The sidebar **Projects** section also has a **New project** button, but only while you have no projects yet. That section starts collapsed until you expand it or create your first project, so the projects page is the dependable entry point.
+While you have no projects yet, the sidebar **Projects** section also offers a **New project** button. Once you have projects, the section is expanded by default until you collapse it.
+
+Project names are limited to 100 characters. Descriptions are limited to 1,000 characters by default, and duplicate project names are allowed.
+
+The projects page shows each project as a folder card with its description and chat count.
+
+## Instructions
+
+Instructions set the tone, goals, or background for every chat in the project, for example a client's style guide or the course a set of chats belongs to. Platform and safety rules still apply on top of them.
+
+
+
+
+**Open the project workspace** and find the **Instructions** section.
+
+
+
+
+**Choose Edit** to open the instructions dialog.
+
+
+
+
+**Enter the workspace instructions and save.** By default, instructions can be up to 16,000 characters.
+
+
+
+
+Instructions are used for new messages in the project. Changing them does not rewrite earlier messages: replies that were already generated stay as they are, and the next message in any of the project's chats uses the current version. Instructions apply to regular model chats and Agents, and to Assistants conversations.
+
+If the project was changed in another session while the dialog was open, saving shows **This project was changed in another session. Reload to see the latest version.** Reload and reapply your edit.
+
+## Files
+
+Project files give the project's chats documents to reference. When File Search is enabled, chats use relevant excerpts from these files rather than whole files.
-Project names are limited to 100 characters and descriptions to 1000 characters. Duplicate project names are allowed.
+In the workspace **Files** section, choose **Add files** and then:
-The Projects dashboard displays each project as a folder card with its description and chat count.
+- **Upload from device** to upload new documents. Each upload shows **Processing** until it is **Ready**.
+- **Choose from your files** to attach files you have already uploaded.
+
+Only files that were uploaded for File Search (embedded message attachments) and have not expired can be attached, so **Choose from your files** lists those files only. A project holds up to 50 files by default.
+
+Project files are used through File Search, so a chat can read them only when:
+
+- the Agent, or the regular chat, has **File Search** enabled
+- File Search is enabled for the Agents endpoint and the deployment's file search service is available
+
+A file that is deleted or expires stays listed as **Unavailable** and is skipped. Removing a file from the project affects future messages, not chat history.
## Work in a Project
-Open a project to see its description and assigned chats. The workspace toolbar can sort chats by last update or creation date. Use **New chat in _project_** to open a scoped composer; its project chip confirms that the new conversation will be assigned to that project. The sidebar shows the same action on each project row.
+Open a project to see its description, instructions, files, and assigned chats. Select the project name or description to edit it in place. The **Chats** list in the workspace can be sorted by last update or creation date.
+
+Use **New chat** in the workspace header to open a composer scoped to the project; its project chip confirms that the new conversation will be assigned to it. The sidebar shows the same action on each project row.
+
+Chats that belong to a project show a project badge with the project name at the top of the conversation. Select the badge to open the project workspace.
Each project chat has an overflow menu with these actions:
@@ -64,19 +112,25 @@ Each project chat has an overflow menu with these actions:
## Move Existing Chats
-Open a conversation's menu and choose **Change project**, then select the project to assign it to. To take a chat back out, use **Remove from project**, which is only shown for chats that already belong to one.
+Open a conversation's menu and choose **Change project**, then select the project to assign it to. To take a chat back out, use **Remove from project**, which is only shown for chats that already belong to one. On desktop you can also drag a chat onto a project in the sidebar; see [Sidebar and Navigation](/docs/features/navigation#desktop-sidebar).
+
+Project context is read again for every message, so a chat you move into a project picks up the project's instructions and files on its next message, and a chat you remove from a project stops using them.
+
+In the sidebar, each project row expands to list its own chats, with **Show all** for longer lists. The **Chats** section lists only conversations that are not in a project, so a project chat appears in one place. Three cases still list every chat:
-A project workspace lists only the chats assigned to that project. The sidebar **Chats** list is unfiltered and keeps showing every conversation, including the ones assigned to projects.
+- while you are searching conversations
+- in the **Archived chats** view of the [chat list menu](/docs/features/navigation#filter-and-sort-the-chat-list)
+- when your projects fail to load
## Manage Projects
-Each project appears in the sidebar. Use the project menu to:
+Use a project's menu in the sidebar to:
-- Open the project workspace
-- Edit the project name and description
-- Delete the project
+- **Open project**
+- **Edit project**, which opens the workspace with the name and description ready to edit
+- **Delete** the project
-The projects page and the workspace header expose the same edit and delete actions.
+The projects page offers the same edit and delete actions, and the workspace header has a project options menu with **Delete project**.
Deleting a project removes the project assignment from its chats. The conversations themselves are not deleted.
diff --git a/content/docs/features/search.mdx b/content/docs/features/search.mdx
index 5cd75b127..6ef578f48 100644
--- a/content/docs/features/search.mdx
+++ b/content/docs/features/search.mdx
@@ -16,6 +16,8 @@ The integration lets users:
- Use typo tolerance and fast as-you-type results across conversation history
- Search messages and shared-link candidates with page sizes above Meilisearch's old 20-result request default
+To narrow the chat list by date, endpoint, attachments, sharing, or bookmarks without typing a query, use the [chat list menu](/docs/features/navigation#filter-and-sort-the-chat-list).
+
LibreChat combines conversation-index and message-index matches before loading the visible conversation page. Searches remain bounded by Meilisearch's configured `pagination.maxTotalHits`; the default ceiling used by LibreChat queries is 1,000 hits. See the [Meilisearch Configuration Guide](/docs/configuration/meilisearch) for setup, synchronization, and reindexing behavior.
diff --git a/content/docs/features/settings.mdx b/content/docs/features/settings.mdx
index 52f9bbeeb..896aceb3b 100644
--- a/content/docs/features/settings.mdx
+++ b/content/docs/features/settings.mdx
@@ -18,10 +18,28 @@ Both regional preferences are stored in the current browser, so different device
**Show chat beside the sidebar on mobile**, under **Layout**, is off by default. When enabled, the mobile drawer stops at 80% of the viewport and leaves a strip of the current conversation visible. Tapping that strip closes the drawer. The preference is stored in the current browser; see [Mobile Drawer](/docs/features/navigation#mobile-drawer).
-**Archived chats** opens the archived-conversation manager. Newly archived chats are ordered by archive time; older records without an archive timestamp fall back to their creation date. Archiving or restoring a chat does not count as new conversation activity. See [Sidebar and Navigation](/docs/features/navigation#archived-conversations).
+**Show composer tips**, under **Layout**, is off by default and stored in the current browser. Turn it on to show keyboard shortcut hints in the message composer.
+
+**Archived chats** is in the account menu rather than this tab; see [Archived Conversations](/docs/features/navigation#archived-conversations).
+
+### Notifications
+
+
+ Newer than **v0.8.8**. Available now on LibreChat's `dev` and `canary` branches, and in the next release.
+
+
+The **Notifications** section controls how LibreChat tells you about replies that finish while you are elsewhere:
+
+- **Show unread count in the tab title** shows the number of conversations with unread replies in the browser tab title and icon. It is on by default.
+- **Notify me when a reply arrives** shows a desktop notification. It is off by default, and turning it on asks for the browser's notification permission. Notifications appear only while you are away from LibreChat, since the chat list already marks the reply when you are looking at it.
+- **Play a sound for new replies** plays a short chime when a reply arrives while you are away. It is off by default. Browsers block audio until you interact with the page, so the first chime after opening a tab may not play.
+
+These preferences are stored per device, so a laptop and a phone can differ; the unread state itself syncs across devices (see [Unread Replies](/docs/features/navigation#unread-replies)). An administrator can turn any of them off for the deployment with [`interface.replyNotifications`](/docs/configuration/librechat_yaml/object_structure/interface#replynotifications), which hides that toggle.
## Chat
+**Switch to Chat History on new chat**, under **Conversations**, is on by default and stored in the current browser. Starting a new chat from the sidebar while another panel, such as Prompts or Memories, is open switches the sidebar back to the chat list. Turn it off to keep the current panel.
+
**Auto-Scroll to latest message on chat open** moves a newly opened conversation to its newest rendered message. Turn it off to preserve the current scroll position when returning to a chat. LibreChat waits for the target conversation rows to render before landing, including when navigation and message loading finish at different times.
**Resize images before upload** lets a user downscale large JPEG, PNG, and WebP files in the browser before sending them. The preference defaults to off and is stored in the current browser.
@@ -34,6 +52,10 @@ Administrators control whether this remains a personal choice. If [`fileConfig.c
**Parsing LaTeX in messages** is enabled by default. The toggle controls the ambiguous single-dollar `$...$` inline-math form, which follows boundary rules that keep currency-like text such as `$50` literal. Unambiguous `$$...$$`, `\(...\)`, and `\[...\]` math remains supported when the toggle is off. Inline code, fenced code, and automatic links are not interpreted as single-dollar math.
+## Model Parameters
+
+The model **Parameters** panel in the side panel groups a conversation's settings under headings: **Identity**, **Sampling**, **Limits**, **Reasoning**, **Context**, **Advanced**, and **Misc.** Only sections that the current endpoint uses appear, and settings the panel does not recognize, such as a custom endpoint's own parameters, are listed under **Misc.** A heading shows how many settings in that section the conversation has changed, so you can see what differs before choosing **Reset**. **Reset** and **Save As Preset** sit together at the bottom of the panel. See [Presets](/docs/user_guides/presets).
+
## Keyboard Shortcuts
Open **Keyboard Shortcuts** from the account menu to review or customize bindings. The **Keyboard Shortcuts** switch is enabled by default. Turning it off disables every shortcut and removes shortcut hints from the interface while preserving custom bindings. Open the same dialog from the account menu to turn shortcuts back on.
diff --git a/content/docs/features/temporary_chat.mdx b/content/docs/features/temporary_chat.mdx
index 1c05d5ae6..a1d4475b3 100644
--- a/content/docs/features/temporary_chat.mdx
+++ b/content/docs/features/temporary_chat.mdx
@@ -1,98 +1,232 @@
---
-title: Temporary Chat
+title: Temporary Chat & Data Retention
icon: Clock
-description: Temporary chats keep selected conversations out of your chat history, search results, and bookmarks for a private, focused experience.
+description: Keep individual conversations out of your history and search with temporary chats, and let administrators expire conversation data automatically or make every chat temporary.
---
Temporary chats let you ask something without keeping it. Use them for sensitive topics, quick experiments, or anything you don't need to save. A temporary chat stays out of your history sidebar, never appears in search, can't be bookmarked, and is deleted automatically once its retention period ends (30 days by default).
-## Start a Temporary Chat
+The same mechanism powers **data retention** for administrators: LibreChat can put an expiry date on every conversation, or force every chat to be temporary for the whole deployment.
-
+
+ [Using Temporary Chat](#using-temporary-chat) is for everyone. [Data retention](#data-retention)
+ onward is for administrators configuring `librechat.yaml`. Each option is also listed in the
+ [interface reference](/docs/configuration/librechat_yaml/object_structure/interface#temporarychat).
+
+
+## Using Temporary Chat
+
+### Start a temporary chat
+
+Start a new chat, then turn on **Temporary Chat** before you send the first message:
+
+- **Desktop:** select the **Temporary Chat** button (a hat and glasses icon) at the right end of the chat header. Hovering it shows the keyboard shortcut.
+- **Mobile:** open the header menu and select **Temporary Chat**. A check mark shows it is on.
+- **Keyboard:** press `Ctrl+Shift+Y` (`Cmd+Shift+Y` on macOS).
+
+
+
+While it is on, the landing view shows **Temporary Chat** with the note "This chat won't appear in your history and will be deleted automatically.", and the message box is tinted. Select the button again to turn it off.
+
+
+
+The toggle is only available before the first message. Once the conversation starts, a read-only **Temporary Chat** badge stays in the header so you always know the chat won't be kept.
+
+### Make every new chat temporary
+
+To start all new chats as temporary, open **Settings > Chat** and turn on **Temporary Chat by default**. This preference is stored in your browser, so set it on each device you use.
+
+### What a temporary chat does
-
+- It does not appear in the chat history sidebar.
+- It is excluded from search results.
+- It cannot be bookmarked.
+- It does not get an automatically generated title.
+- It is kept until its retention period ends, then deleted automatically. The default period is 30 days; your administrator may set a different one.
-**Open the model menu.** Select the model dropdown at the top left of the chat view.
+Retention applies to conversations, messages, files and shared links. [Memories](/docs/features/memory) are not deleted when a temporary chat expires.
-
+
+ If your administrator has made every chat temporary, the **Temporary Chat** button is always on
+ and can't be turned off. Hovering or focusing it shows "Temporary Chat is enabled for all chats by
+ your administrator", and the keyboard shortcut does nothing.
+
+
+## Data retention
+
+Administrators control retention with a few options under `interface` in `librechat.yaml`. The main one is `retentionMode`, which decides which chats get an expiry date:
-
+| Mode | What expires | Chats visible in history and search? | Can users opt out? |
+| ----------------------- | ------------------------------------ | --------------------------------------- | -------------------- |
+| `temporary` _(default)_ | Only chats a user marks as temporary | Yes for normal chats, no for temporary | Yes, chat by chat |
+| `all` | Every chat | Yes, until each chat expires | No |
+| `ephemeral` | Every chat | No, every chat is temporary | No, the toggle is locked on |
-**Turn on Temporary Chat.** Toggle the **Temporary Chat** switch to the ON position.
+The default is `temporary`, so existing deployments keep their current behavior until you change it. The `ephemeral` mode is newer than **v0.8.8**; it is available on LibreChat's `dev` and `canary` branches and ships in the next release.
-
-
-
+### Configuration
-
+```yaml filename="librechat.yaml"
+interface:
+ # Who may use the Temporary Chat toggle (seeds the TEMPORARY_CHAT permission for the USER role)
+ temporaryChat: true
-
+ # Which chats expire: "temporary" (default), "all", or "ephemeral"
+ retentionMode: 'temporary'
-**Confirm it's active.** Before the first message, the landing view identifies the session as a Temporary Chat and explains that it stays out of history and is deleted automatically. After the conversation starts, a read-only indicator remains in the header even though the setup toggle is no longer available.
+ # Lifetime of temporary chats, in hours. Default 720 (30 days), range 1 to 8760.
+ temporaryChatRetention: 720
-
+ # Lifetime of regular chats under retentionMode "all", in hours. Defaults to temporaryChatRetention.
+ # generalChatRetention: 2160
-
+ # Under "all" or "ephemeral", keep files uploaded to agents (knowledge and context files) from expiring.
+ retainAgentFiles: false
+```
-## What a Temporary Chat Does
+| Key | Type | Default | Description |
+| ------------------------ | ----------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `retentionMode` | `"temporary"` \| `"all"` \| `"ephemeral"` | `"temporary"` | Which chats receive an expiry date. See [The three modes](#the-three-modes). |
+| `temporaryChatRetention` | number (hours) | `720` | How long a temporary chat is kept after it was last saved. Also the lifetime of every chat under `ephemeral`. Range `1` to `8760`. |
+| `generalChatRetention` | number (hours) | `temporaryChatRetention` | How long regular chats are kept under `all`. Ignored by the other modes. Range `1` to `8760`. |
+| `retainAgentFiles` | boolean | `false` | Under `all` and `ephemeral`, files uploaded to an agent's resources don't expire. Files attached to messages still expire with their chat. |
+| `temporaryChat` | boolean | `true` | Seeds the `TEMPORARY_CHAT` permission for the built-in `USER` role at startup. Manage the permission per role in the [Admin Panel](/docs/features/admin_panel). |
-- Does not appear in the chat history sidebar.
-- Is excluded from search results.
-- Cannot be bookmarked.
-- Shows a dedicated landing state before the first message and an active indicator afterward.
-- Is stored in the database until its retention period ends, then deleted automatically.
+A `librechat.yaml` value outside `1` to `8760` hours fails config validation.
-
+#### Environment variable
-The default retention period is 30 days. Administrators can change it. See [Configuration](#configuration-administrators) below.
+The temporary chat lifetime can also be set with `TEMP_CHAT_RETENTION_HOURS`. `interface.temporaryChatRetention` takes precedence when both are set. An invalid value falls back to `720`, and a value outside `1` to `8760` is clamped into that range with a warning in the server log.
+
+ `TEMP_CHAT_RETENTION_HOURS` is deprecated. Use `interface.temporaryChatRetention` instead.
-## Configuration (administrators)
+### The three modes
+
+
+
+ **`temporary` (default).** Only chats a user marks as temporary expire, whether through the toggle or the **Temporary Chat by default** setting. They are hidden from history and search and deleted after `temporaryChatRetention` hours. Normal chats never expire.
+
+
+ **`all`.** Every conversation and message gets an expiry date, but the chat experience doesn't change: chats stay in history and search until they expire.
+
+ - Regular chats use `generalChatRetention`, or `temporaryChatRetention` when it isn't set.
+ - Chats a user marks as temporary still behave as temporary chats and use `temporaryChatRetention`.
+ - Uploaded files and shared links expire too. Agent resource files are kept only with `retainAgentFiles: true`.
-Temporary Chat is available to users by default. Administrators control whether the feature is offered and how long temporary chats are kept.
+ Use `all` for a "keep everything for N days, then delete" policy.
-**Availability** is governed by the `TEMPORARY_CHAT` role permission. Manage it for each role from the [Admin Panel](/docs/features/admin_panel). The `interface.temporaryChat` option in `librechat.yaml` only seeds this permission for the default `USER` role at startup and is deprecated for permission management.
+
+
+ **`ephemeral`.** Every chat is temporary. This is "Temporary Chat always on" for the whole deployment.
-**Retention** is set with `interface.temporaryChatRetention` (in hours). The minimum is 1 hour, the maximum is 8760 (1 year), and the default is 720 (30 days).
+ - Every chat is hidden from history and search, gets no generated title, and is deleted after `temporaryChatRetention` hours. `generalChatRetention` is ignored.
+ - The **Temporary Chat** toggle is locked on for everyone, including users whose role lacks the `TEMPORARY_CHAT` permission. The keyboard shortcut is disabled.
+ - The server enforces it: a client that sends `isTemporary: false` is overridden.
+ - Uploaded files and shared links expire with their chat. Agent resource files are kept only with `retainAgentFiles: true`.
+
+ Use `ephemeral` when no conversation should be kept long term.
+
+
+
-
+### How retention works
-
+Each conversation and message carries an `expiredAt` date, and temporary ones are also flagged with `isTemporary: true`. Hidden and expired records are filtered out of the history list and the search index before they are deleted.
+
+- **Conversations, messages and shared links** are removed by a MongoDB TTL index on `expiredAt`. MongoDB runs this cleanup in the background, usually within about a minute of the deadline.
+- **Files** are removed by a periodic sweep in LibreChat, which deletes the stored file (local disk, S3, and so on) and then its record.
+
+The deadline is set when a record is saved. Every save of a conversation moves its deadline forward, so an active chat stays around while it is in use. Each message keeps the deadline it got when it was saved.
+
+Under `all` and `ephemeral`, retention applies wherever chat data is written: new messages on every endpoint, regenerations and branches, message edits, feedback, artifact edits, resumed responses, forks, duplicates and imports.
+
+- **Forks, duplicates and imports.** Under `ephemeral` they are always temporary. Under `all`, a copy of a temporary chat stays temporary, and other copies and imports stay visible with an expiry date.
+- **Shared links** take their expiry from the conversation they point to, so a link never outlives its chat.
+- **Memories** have no expiry and are not affected by any retention mode.
+
+### Switching modes
+
+Changing `retentionMode` or a retention period only affects records saved afterwards. LibreChat does not rewrite existing deadlines.
+
+- **To `all` or `ephemeral`:** existing chats get a deadline the next time they are written to. Under `ephemeral`, an older regular chat that receives a new message, edit, feedback or branch is converted to a temporary chat on the spot: it leaves history and search and loses its bookmarks. Chats nobody touches keep their current state.
+- **Back to `temporary`:** chats saved under `all` still carry their deadlines and will still be deleted. Chats saved under `ephemeral` remain temporary chats and keep expiring.
+
+To stop regular chats from expiring after leaving `all` or `ephemeral`, clear their deadlines in `mongosh`:
+
+```js filename="mongosh"
+db.conversations.updateMany(
+ { isTemporary: false, expiredAt: { $ne: null } },
+ { $unset: { expiredAt: 1 } },
+)
+db.messages.updateMany(
+ { isTemporary: false, expiredAt: { $ne: null } },
+ { $unset: { expiredAt: 1 } },
+)
+```
+
+
+ MongoDB doesn't drop superseded indexes on its own. Once the new
+ `_meiliIndex_1_isTemporary_1_expiredAt_1` indexes exist on `conversations` and `messages`, you can
+ drop the old `_meiliIndex_1_expiredAt_1` indexes from both collections.
+
+
+### Examples
+
+Delete every chat 90 days after its last activity, and keep chats visible until then:
```yaml filename="librechat.yaml"
interface:
- temporaryChat: true
- temporaryChatRetention: 168 # retain temporary chats for 7 days
+ retentionMode: 'all'
+ generalChatRetention: 2160 # 90 days for regular chats
+ temporaryChatRetention: 24 # 1 day for chats users mark as temporary
```
-
+Make every chat temporary and delete it after 24 hours:
-
+```yaml filename="librechat.yaml"
+interface:
+ retentionMode: 'ephemeral'
+ temporaryChatRetention: 24
+```
-```bash filename=".env"
-# Hours to retain temporary chats (default: 720 = 30 days)
-TEMP_CHAT_RETENTION_HOURS=168
+Make every chat temporary for a week, but keep the files attached to agents:
+
+```yaml filename="librechat.yaml"
+interface:
+ retentionMode: 'ephemeral'
+ temporaryChatRetention: 168
+ retainAgentFiles: true
```
-
+## FAQ
-`TEMP_CHAT_RETENTION_HOURS` is deprecated. Prefer `interface.temporaryChatRetention` in `librechat.yaml`, which takes precedence over the environment variable.
+
-
+
+ When the conversation was last saved. Each new message moves the conversation's deadline forward,
+ so an active chat isn't deleted while it is in use.
+
-
+
+ Not to the second. MongoDB removes expired conversations and messages in a background pass,
+ usually within about a minute, and files are removed by LibreChat's periodic sweep. Expired chats
+ are already hidden from users before then.
+
-
+
+ `all` keeps chats normal and visible, with a delete-by date. `ephemeral` makes every chat
+ temporary: hidden from history and search, with the toggle locked on. Choose `all` for "delete
+ after N days" and `ephemeral` for "never keep conversations".
+
-Common retention values:
+
+ No. The toggle is locked on and the server overrides any request to save a chat as permanent.
+
-| Value | Period |
-| --- | --- |
-| `1` | 1 hour (minimum) |
-| `24` | 1 day |
-| `168` | 1 week |
-| `720` | 30 days (default) |
-| `8760` | 1 year (maximum) |
+
+ No. A shared link expires no later than the conversation it points to.
+
-For the full set of options, including `retentionMode` and `retainAgentFiles`, see the [interface reference](/docs/configuration/librechat_yaml/object_structure/interface#temporarychatretention).
+
diff --git a/content/docs/features/upload_as_text.mdx b/content/docs/features/upload_as_text.mdx
index e3d9ed602..fb5324c3a 100644
--- a/content/docs/features/upload_as_text.mdx
+++ b/content/docs/features/upload_as_text.mdx
@@ -20,14 +20,16 @@ You attach a file, LibreChat extracts the text from it, and the full content get
- ### Click the attachment icon
+ ### Open the palette
- In the chat input bar, click the **paperclip** (📎) icon.
+ Click **+** in the [message composer](/docs/features/composer#add-files). You can also drag the file onto the composer.
### Choose a source
- Select your local machine or SharePoint when it is configured. The default uploader no longer asks you to choose a model or tool destination.
+ In the **Attach** section, select **From Local Computer**, or **From SharePoint** when it is configured. The default uploader does not ask you to choose a model or tool destination.
+
+ If your administrator enabled [`legacyFileUploadUX`](/docs/configuration/librechat_yaml/object_structure/file_config#legacyfileuploadux), choose the destination yourself: select **More upload options**, then **Upload as Text**.
### Choose your file
@@ -44,7 +46,7 @@ You attach a file, LibreChat extracts the text from it, and the full content get
When you select several files, LibreChat skips individual duplicates and files over the per-file size limit, names the skipped files in a notice, and continues uploading the valid files. The file-count and total-batch-size limits still apply to the files that remain; if the surviving batch exceeds either limit, the batch is rejected together. Single-file behavior is unchanged.
- If "Upload as Text" doesn't appear, the `context` capability may have been disabled by your admin. It's on by default — but if the capabilities list was customized, `context` needs to be explicitly included. See the [configuration section](#the-context-capability) below.
+ With `legacyFileUploadUX` enabled, if **Upload as Text** doesn't appear under **More upload options**, the `context` capability may have been disabled by your admin. It's on by default, but if the capabilities list was customized, `context` needs to be explicitly included. See the [configuration section](#the-context-capability) below.
---
diff --git a/content/docs/features/web_search.mdx b/content/docs/features/web_search.mdx
index a466d69df..e62afe373 100644
--- a/content/docs/features/web_search.mdx
+++ b/content/docs/features/web_search.mdx
@@ -268,7 +268,7 @@ If the admin hasn't configured all the necessary API keys, users will be prompte
Once configured, you can use web search in two ways:
-1. **Chat Interface**: Click the web search button in the chat interface to enable web search for your conversation
+1. **Chat Interface**: Open the **+** palette in the [message composer](/docs/features/composer) and select **Web Search** to enable web search for your conversation
2. **Agents**: Use the `web_search` capability in agents to allow them to search the web
## Notes
diff --git a/content/docs/mcp_servers/google_workspace.mdx b/content/docs/mcp_servers/google_workspace.mdx
index fe41337d1..1cb755e18 100644
--- a/content/docs/mcp_servers/google_workspace.mdx
+++ b/content/docs/mcp_servers/google_workspace.mdx
@@ -314,7 +314,7 @@ docker logs LibreChat --tail 200 | grep MCP
### Connect each server in LibreChat
-Open LibreChat, then open **MCP Settings** or the **MCP Servers** dropdown in the chat input.
+Open LibreChat, then open **MCP Settings** or the **MCP Servers** section of the [composer's **+** palette](/docs/features/composer#turn-on-tools-skills-and-mcp-servers).
For each Google Workspace server:
diff --git a/content/docs/mcp_servers/salesforce.mdx b/content/docs/mcp_servers/salesforce.mdx
index 923069e2d..53fdc4048 100644
--- a/content/docs/mcp_servers/salesforce.mdx
+++ b/content/docs/mcp_servers/salesforce.mdx
@@ -240,7 +240,7 @@ docker logs LibreChat --tail 200 | grep MCP
### Connect Salesforce in LibreChat
-Open LibreChat, then open **MCP Settings** or the **MCP Servers** dropdown in the chat input.
+Open LibreChat, then open **MCP Settings** or the **MCP Servers** section of the [composer's **+** palette](/docs/features/composer#turn-on-tools-skills-and-mcp-servers).
1. Click **Connect** for the Salesforce server.
2. Complete the Salesforce OAuth flow.
diff --git a/public/images/composer/composer-palette.png b/public/images/composer/composer-palette.png
new file mode 100644
index 000000000..1b65bf98d
Binary files /dev/null and b/public/images/composer/composer-palette.png differ
diff --git a/public/images/mcp/mcp_servers_catalog.png b/public/images/mcp/mcp_servers_catalog.png
new file mode 100644
index 000000000..0b5554960
Binary files /dev/null and b/public/images/mcp/mcp_servers_catalog.png differ
diff --git a/public/images/temporary-chat/temporary-chat-active.png b/public/images/temporary-chat/temporary-chat-active.png
new file mode 100644
index 000000000..931b8b575
Binary files /dev/null and b/public/images/temporary-chat/temporary-chat-active.png differ
diff --git a/public/images/temporary-chat/temporary-chat-button.png b/public/images/temporary-chat/temporary-chat-button.png
new file mode 100644
index 000000000..b836c72e0
Binary files /dev/null and b/public/images/temporary-chat/temporary-chat-button.png differ