Bound model-facing Flux images to the 5 MiB base64 budget for standard and finetuned generation. Preserve original image data for attachment storage under the existing resolution policy.
Fixes#6954.
* fix: Read the Account and Session Together and Test Cancel on a Real Database
- Start the setup user and refresh session lookups together in the cancel controller instead of one after the other, as the opt-in confirmation already does
- Run the cancel controller spec on mongodb-memory-server with the real data-schemas methods, so the session and compare-and-swap predicates under test are the production ones, and add a negative case for each
- Keep only a module wiring test in the CJS controller spec, since the database behavior is now covered where it can run for real
* test: Pin the Cancel Response When the Session Lookup Fails
Both lookups now run before any cookie or write, so a failed session lookup answers 500 with the browser and the stored state untouched. Cover that on the real database.
* fix: Keep the Saved Files Draft Until the File List Loads
After a reload the live file map is empty until the server's file list arrives, and the restore that rebuilds it needs that list. The save effect rewrote the stored draft from the empty map in between, so a pasted-text chip or any other attachment was gone before it could be restored. It now waits for the list. This is what turned the composer-attachments restore scenario red on dev.
- Bump @floating-ui/utils from 0.2.8 to 0.2.9 in package-lock.json and bun.lock so isContainingBlock treats translate, scale and rotate as containing blocks
- Add schedule-time-popover scenarios: the Weekly time picker, the Hourly minute picker and a dialog scrolled by its own overflow each open against their field
* fix: Bound Generation Status Reads So a Silent Request Cannot Strand the Pane
* test: Cover Status Reads That Never Answer Across Terminal, Error and Foreground Paths
* test: Match Scenario Replies Exactly
* fix: Keep the Provider Message on Classified Rate Limit Errors
* fix: Honor maxProviderErrorChars for Classified Generation Errors
* test: Cover Classified Gateway Errors in the Mock Harness
* test: Cover a Zero Provider Detail Limit on Generation Errors
* refactor: Resolve Classified Provider Errors in the TypeScript Backend
* fix: Redact Provider Details Before Truncating Them
* fix: Wrap Label and Prose Text Between Words Instead of Mid-Word
* test: Cover Word Wrapping in the MCP Trust Disclosure and Bookmark Delete Dialog
* test: Reopen the MCP dialog per width and treat hyphens as break points
* test: Open the bookmark delete confirmation from the keyboard
* fix: Let the MCP Dialog Segmented Controls Wrap on Narrow Screens
* 📏 fix: Measure Usage Percent Against the Allowance Each Period Granted
Store the credits granted at the start of each balance period on the record and measure the usage percent against it, so changing refillAmount or startBalance no longer moves a user's percent until their next grant. Legacy records fall back to the configured amount.
* 🕰️ fix: Ignore a Stored Allotment a Later Refill Has Replaced
Stamp each stored allotment with the time of its grant and measure against it only while no refill is more recent, so a refill by a replica that predates the field falls back to the configured amount instead of reporting usage against the prior period's allowance.
* 🔒 fix: Write the Allotment Grant Atomically and Return Its Period Start
Insert lastRefill with the credit, allotment and grant time when a balance record is created, so a racing creator never moves another's period start past its grant. Return lastRefill even without auto-refill so the client can place a stored allotment in its period, and fall back when it cannot.
* 🔐 fix: Authorize Restored MCP Tools Before Agent Revert Writes Them
Reverting wrote the version snapshot, including its mcpServerNames, before
filterAuthorizedTools ran. Agent-scoped MCP access is granted from that index,
so a restored server vouched for itself and an editor without access to it
could re-attach it. A snapshot without mcpServerNames also left the current
index in place, keeping agent-scoped access to servers no tool used.
The handler now authorizes the restored tools against the agent as it stands,
treating only its current tools as existing, and passes the authorized tools
and rebuilt index to revertAgentVersion so the restore is one write pinned to
the revision it was checked against. revertAgentVersion always rewrites the
index for the restored tools.
* 🔐 fix: Exclude the Target Agent's Own Grant When Authorizing a Revert
Moves the restore authorization into authorizeAgentRestore in packages/api,
leaving the revert controller as wiring.
A newly restored MCP tool whose server the user reaches only through agents
must now be reachable through an agent other than the one being reverted,
checked fresh against the database. The target's current or cached index
(including one left stale by an earlier revert) no longer vouches for it.
Prior server names are matched through their normalized aliases, and only for
tools the agent already had. When nothing resolves, the pre-restore index is
kept instead of deriving names from tool keys.
A revision-filtered restore that misses is reported as a 409 conflict.
The route spec restores the real ~/config so the MCP registry is consulted
rather than reported unavailable.
* 🔐 fix: Check Newly Restored MCP Servers Against Live Grants
The restore gate now runs for every approved MCP tool, not only consumeOnly
ones. A tool the agent doesn't already have, on a DB-backed server, is
admitted when a current tool still uses that server, or on a live direct
server grant, or through another viewable agent. A grant the registry still
caches after revocation no longer becomes durable through the restored index.
Admission is memoized per server. A snapshot tools array that sanitizes to
nothing restores an explicit empty set. Active-server matching no longer
reuses the index fallback, so a stale entry is never read as in use.
* 🔐 fix: Treat Legacy Cached DB Configs as User-Sourced in the Restore Gate
A config cached before an upgrade may carry a dbId without source. The
restore gate now uses isUserSourced, so such configs get the live access
check instead of passing as operator-managed.
* 🔐 fix: Recheck Unindexed Existing MCP Tools in the Restore Gate
Holding a tool no longer exempts it from the live check. Only a current tool
whose server the agent's index already lists skips it. Otherwise an unindexed
tool on a legacy agent could pass on a revoked grant the registry still
caches, and the restore would add its server to the index.
* ⚡ perf: Run Restore Admission Checks for Distinct Servers Concurrently
filterAuthorizedTools now starts every admission check during its single pass
and awaits them together, so each distinct DB-backed server no longer adds a
serial lookup wave to the revert. Tool order is preserved and server names
are recorded only for admitted tools.
* perf: Prefetch Conversation Messages From the Chat Route
The messages request started only when ChatView mounted, after startup config, roles, endpoints, models and the conversation record resolved, although it needs only the route id. ChatRoute now starts it as soon as the id is known for a conversation the cache has never held, sharing the view's query function, and the view's first mount uses that payload instead of fetching it again.
* fix: Keep the Messages Prefetch Mark for the Observer That Uses It
Child observers of the same conversation mount first and cleared the mark before the conversation view subscribed, so the view loaded the messages a second time.
* fix: Show a Failed Messages Prefetch Instead of Requesting It Again
A prefetch that failed left no data, so the view loaded the messages again on mount, and a link to a missing conversation sent two requests. The view now shows a fresh prefetch failure, and the prefetch retries like the view's own load except on a 4xx.
* test: Cover Route-Level Messages Loading on Cold Load, Sidebar Switch and Missing Conversation
* fix: Retry Rate-Limited and Timed-Out Messages Prefetches
* fix: Prove Prefetched History by Reply Identity and Honor Invalidation
A URL load's prefetch starts before the conversation record is cached, so it captured no reply stamp and the seen check loaded an unread conversation's history a second time. Once the record lands, the prefetched history is proven when it holds the exact reply the record names. The mount guards also yield to an invalidation, and the failed-prefetch guard is the default for every observer of the conversation's messages.
* fix: Match Vendor Chunk Rules Against the Module Path, Not the Checkout
The vendor chunk rules tested the absolute module path, so a repository under a directory whose name contains a rule's text (fetch, ky, zod) sent every dependency to that chunk and the PWA precache limit failed the build. The rules now see the path from the first node_modules segment on.
* test: Cover Vendor Chunk Rules Under a Hostile Checkout Path
* refactor: Move Part Helpers and activityLabels Into the Chat Package
activityLabels moves to packages/chat/src/core/parts and exports from
@librechat/chat. useLazyCollapseBody, useProgress and useThrottledValue move
to packages/chat/src/react/parts (@librechat/chat/react). useExpandCollapse,
CopyButton, ActionButton, LangIcon and its icon paths move to
packages/chat/src/components/parts (@librechat/chat/components), since they
read @librechat/client. Specs move with them, client importers and mocks point
at the package entries, and design-lint suppressions follow the moved files.
The fold-type set in activityLabels is built on first use so the module reads
no data-provider enum at load, with a spec pinning its members.
* test: Point the About Spec's Localize Mock at the Package's Copy Button
* chore: Retest Against Current Dev
* fix: Only Hand Out OpenID Access Tokens the Bearer Verifier Accepts
The OpenID reuse path in refreshController and the post-refresh fallback in
getOpenIDAppAuthToken returned the cached access token as the app auth token
whenever its exp was in the future. Entra issues Microsoft Graph access tokens
to sign-in clients by default; their aud names Graph, so the bearer verifier
rejects them and every protected request answers 401 until the client loops.
Both paths now require the access token's aud to name the client id or an
OPENID_AUDIENCE entry, the same audiences the verifier accepts. The reuse path
refreshes with the IdP instead, and the post-refresh fallback keeps opaque
tokens as before. Audience parsing is shared with openIdJwtStrategy.
Related to #17791
* test: Exercise Real OpenID Audience Helpers in AuthService Spec
The AuthService spec stubbed the audience helpers as pass-throughs, so it
could not catch a foreign-audience access token escaping the post-refresh
fallback. Use the real exports and cover Graph-audience, configured-audience
and opaque access tokens through setOpenIDAuthTokens.
* fix: Require a Click to Submit Email Verification Links
Mail security scanners (Microsoft Defender Safe Links, Sophos, Hornetsecurity)
open links in incoming mail and run the page's scripts, so the verify page
submitted the single-use token on load and the user's own click then failed.
The page now waits for an explicit confirm button for both sign-up
verification and email-change confirmation.
* test: Cover verification links opened before the user clicks
* test: Isolate the verify click scenario from the submission limiter
* test: Confirm the sign-up link in the email e2e setup
* test: Exercise the shared Button in the verify page spec
* fix: Skip Attachments Deleted by Storage Retention Instead of Failing the Turn
A file that storage retention removed from an earlier message made every
later turn of the conversation fail, because the encoders rethrew the
storage miss and the user has no way to remove an attachment from history.
History replay now passes a handler to the encoders, which leave missing
files out of the request and report them; the message's file context tells
the model which files were deleted and were not read. Current-turn
attachments keep the strict error, since the user can remove and re-upload
them. Code Interpreter priming names files missing from storage in the
tool context and only requires resource recovery for other failures.
* refactor: Keep Code Reupload Failure Policy in packages/api
Tally priming reupload failures in createCodeReuploadFailures so the
decision that a file deleted from storage is named instead of requiring
resource recovery lives in TypeScript, and add mock e2e scenarios for a
follow-up after an attachment is deleted from storage.
* fix: Classify Code Files as Deleted Only at the Storage Read
A 404 from the Code API upload was counted as a source file deleted from
storage, which skipped resource recovery. openCodeFileSource marks a miss
where the stored object is read, and only that error counts as deleted.
Move the history replay of attachments with tolerated misses into
replayMessageAttachments so BaseClient only calls it.
* fix: Name Local Code Files Whose Stream Reports a Missing Object
Local storage returns a read stream that raises ENOENT only once read, so
openCodeFileSource now holds a stream until its first chunk or error and
names a miss there. The missing-files context assignment moves into
setMissingCodeFilesContext so both tool loaders only call it.
* fix: Close a Held Code File Stream When Priming Is Cancelled
openCodeFileSource waits for a source stream's first chunk; when that
wait fails, as on cancellation, the caller never receives the stream, so
destroy it before rethrowing instead of leaking its file descriptor.
* fix: Treat Empty Sandpack Bundler URLs as Unset
An empty SANDPACK_BUNDLER_URL or SANDPACK_STATIC_BUNDLER_URL (common with
${VAR:-} passthrough in Docker Compose) was handed to Sandpack as the
bundler URL, leaving artifact panels blank. buildSandpackOptions now falls
back to the default bundler for an empty value, and the shared-link startup
payload omits empty bundler URLs.
Related to #17790
* test: Cover Empty Sandpack Bundler URLs in Artifact Previews
* fix: Poll the Flux Cluster polling_url Returned by the Global Endpoint
* fix: Only Forward the Flux Key to Same-Origin or BFL Polling URLs
* test: Cover Flux polling_url Routing in the Mock Harness
* fix: Match mcp:<server>:<tool> Tool Approval Patterns Against MCP Tool Keys
* fix: Normalize Globbed MCP Server Names in Tool Approval Patterns
* fix: Keep Colons Inside MCP Server Names in Tool Approval Patterns
* test: Submit the Scoped Approval and Give the Control Agent remember_fact
* fix: Match mcp:<server>:<tool> Rules Per Segment Instead of Rewriting Them
* fix: Match Glob-Free mcp: Approval Rules Against Their Exact Key
* fix: Never Grant Through a Server Glob That Normalization Widened
* fix: Never Grant a Globbed mcp: Rule on an Ambiguous Tool Key
* fix: Mirror Both Server Name Normalization Branches in mcp: Server Globs
* fix: Treat Boundary Underscores in mcp: Server Globs as Optional
* fix: Refresh Copied Client Bundle Files After Build
fs.cp keeps the source mtime on Windows, so dist/tokens.css and the other
copied theme files never looked newer than packages/client inputs written
later by a checkout, and the pre-commit freshness check failed after every
rebuild. Stamp the copied outputs in tsdown's build:done hook.
Related to #17731
* test: Cover copied client bundle freshness where cp keeps mtimes
* fix: Accept Content-Array Outputs in Responses function_call_output
The Open Responses spec allows function_call_output.output to be a string or
an array of input_text, input_image and input_file parts, but request
validation refused anything that was not a string. Validate the array parts
and convert them with the same part mapping the message path uses, so the
replayed tool result keeps its text and images in order.
* fix: Inspect Tool-Output Media Against File Policies
Image URLs and file names in a function_call_output content array reached the
model without the file-policy fragments the same parts get in a message.
Collect those fragments for both item types in packages/api and call that
from the Responses controller.
* test: Cover Content-Array Tool Outputs in the Responses API
* fix: Remember Tool Approvals for MCP Servers Using Env References
* fix: Resolve Approval Env Only for Tiers That Expand It, Add E2E Scenarios
* fix: Reject User Placeholders in Resolved Env Values and Take the Env Source as a Parameter
* test: Expect the Resume Reply After Approving the Env MCP Tool
* fix: Check Fully Expanded Config Values for User Placeholders
* fix: Resolve Approval Env Into a Prototype-Free Map From Own Properties