Conversation
Social providers that do not return a profile picture cause sign-in to fail when a non-local file strategy is configured. `handleExistingUser` and `createSocialUser` pass the incoming `avatarUrl` straight to `resizeAvatar`, which throws "Invalid input type. Expected URL, Buffer, or File." on null. `socialLogin` catches that error and forwards it to the Passport callback, so authentication fails instead of the avatar simply being skipped. Apple is affected by definition: `appleStrategy.js` sets `avatarUrl: null` with the comment "Apple does not provide an avatar URL". Any Google or OIDC account without a picture reaches the same path. Guard the three call sites so that a missing avatar skips avatar processing and leaves the rest of the sign-in flow untouched. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* 🧬 fix: Collapse Radix Layer Packages to One Copy Radix coordinates nested layers and focus scopes through module-level state, so a dialog and a popover inside it cooperate only while they import the same copy of `react-dismissable-layer` / `react-focus-scope`. This tree had four and three of them: dialog -> react-dialog/node_modules/… 1.0.2 (exact pin) select -> react-select/node_modules/… 1.1.10 menu -> react-menu/node_modules/… 1.1.0 hover -> hoisted 1.0.5 Split that way, a popover never learns the dialog disabled body pointer events and the dialog's focus trap is never paused, so in-dialog selects and hover cards are inert and keyboard-dead (LibreChat-AI#15738). The exact `1.0.2` pin on `react-dialog` and `react-alert-dialog` came from LibreChat-AI#11023, which downgraded them from ^1.1.15 to fix modal tooltip and dropdown regressions — the same split seen from the other side, when the dialog was the new package and everything else was old. Unpinning both and refreshing the other Radix packages inside their existing carets collapses the tree to one copy of each. No `overrides` entry: the resolution stands on its own, and pinning transitive versions would only hide the next split. `radixLayers.spec.ts` reads the lockfile and fails if either package is ever nested again; `dialogPopoverFocus.spec.tsx` asserts the behaviour that invariant exists for — a portaled option holding focus inside a dialog, which fails on the old tree and passes on this one. Verified with a real install in a worktree: one copy of each resolved from dialog, select and hover-card alike; `packages/client` 46 suites / 476 tests and `client` 323 suites / 3201 tests green; both workspaces typecheck. Needs a manual pass over the app's modals before merge — what LibreChat-AI#11023 was guarding cannot be proven by jsdom. * 🍞 fix: Give Escape Back to the Dialog Under a Toast Radix routes Escape to the highest dismissable layer only, and `Toast.Root` registers one — mounted after whatever dialog is already open. Splitting `react-dismissable-layer` into per-package copies hid this: toasts and dialogs were never in the same stack. Collapsing them to one copy puts a status toast above every open dialog, and it eats the Escape meant for the dialog beneath. Measured in the running app (mock e2e, instrumented): the dialog's own `onEscapeKeyDown` was never called, the layer above it was `<li class="toast-root" data-radix-collection-item>`, and a SECOND Escape closed the dialog. That is `agent-skills.spec.ts:709`, which failed deterministically here across three attempts. The frontmost open dialog now takes Escape back while a toast is present, closing through a hidden `Dialog.Close` so controlled and uncontrolled dialogs behave alike. Scoped tightly: no toast on screen leaves Radix's arbitration untouched, and only the frontmost dialog acts, so an inner dialog still closes alone. The toast is matched whatever its `data-state`. A toast that has begun closing keeps its layer registered until the exit animation ends, and that is exactly the case that failed — the first attempt keyed on `[data-state="open"]` and missed it, which the instrumentation caught (`FB toast= null allLis= 1`). `agent-skills.spec.ts` now passes locally on the deduped tree: 4 passed. Regression test verified to fail with the fix reverted. * 🚪 fix: Close the MCP Menu With the Dialog It Opened Ariakit takes Escape for a menu only when the event target is the menu, its trigger, or `body`: if (event.key !== "Escape") return; if (event.defaultPrevented) return; ... if (isElement(target) && target.tagName === "BODY") return true; if (contains(dialog, target)) return true; if (contains(disclosureElement, target)) return true; return false; // ariakit dialog.tsx:496-517 While the MCP config dialog holds focus none of those is true, so the Escape that closes the dialog leaves the menu open behind it — and `disabled={isOpen}` on the trigger then makes it unclickable. The reader is stranded with a menu they cannot reach and a button they cannot press; `mcp-oauth-readiness` times out clicking it, three tests over. Keyed on the dialog CLOSING, not opening. The first attempt closed the menu as the dialog opened and broke a fourth test that reads the server rows from the still-mounted menu underneath (`unmountOnHide` deletes them): `element(s) not found`. Closing on the way back out leaves that untouched. Local mock e2e on the deduped tree: `mcp-oauth-readiness` 5 passed (was 2/5), `agent-skills` + `mcp` 5 passed, `client` 3204 tests green, typecheck clean. * 🛡️ fix: Keep the Escape Fallback Inside Its Lane Codex found three, all real. **P1 — the fallback overruled the guards it should obey.** Any Escape while a toast was up clicked the dialog's close button, including when a select inside the dialog owned that Escape (WCAG 2.1.1) or when another layer had already answered it. A reader closing a listbox would have lost the whole dialog and anything unsaved in it. It now bails on `event.defaultPrevented` and on the same popup conditions the Radix-provided handler honours — extracted into one `escapeBelongsToPopup` helper both call, so they cannot drift apart. **P2 — the frontmost check read inline z-index only.** `ImagePreview` is a raw `DialogPrimitive.Content` carrying `z-[250]` as a CLASS, so it scored zero and the My Files dialog underneath was mistaken for the frontmost — Escape in the preview would have closed it too. Ranking now reads the resolved `getComputedStyle().zIndex`. **P2 — the published contract did not carry the singleton.** `@librechat/client` still admitted `react-toast@1.1.5` and `react-popover@1.0.7`, which depend on the older dismissable-layer/focus-scope line, so a consumer could reassemble the very split this PR removes. Radix minimums raised to the versions the lockfile resolves; the tree still holds exactly one copy of each. Three regression tests, each verified to fail against the pre-fix component. `packages/client` 484 tests, `client` 3204 tests, both typecheck clean, local mock e2e `agent-skills` + `mcp-oauth-readiness` 9 passed. * 🎚️ fix: Let the Consumer and the Alert Dialog Have Their Say Two more from codex, both real. The fallback never ran the dialog's own `onEscapeKeyDown`. Radix calls that only for the highest layer, which the toast is — so a dialog whose consumer cancels Escape (`event.preventDefault()`) closed anyway, taking whatever was in it. The fallback now invokes the supplied handler and honours the cancellation, exactly as the Radix path does. The frontmost ranking looked only for `role="dialog"`. `OGDialogContent` is also used as an alert dialog — the shared-link delete confirmation (`SharedLinkButton.tsx:426`) — so its content was never found, `frontmost` stayed null, and the fallback declined to act: that confirmation still needed two Escapes. Alert dialogs are ranked too. Two more regression tests, both verified to fail against the previous version. `packages/client` 486 tests, `client` 3204 tests, typecheck and static checks clean.
…ialog (LibreChat-AI#15751) Two findings from the last codex round on LibreChat-AI#15742, which merged before these landed. Both are real; the third report was the same ref finding filed three times. The ref wrapper `OGDialogContent` gained there swallows a callback ref's return value. Under the React 19 peer range a consumer's teardown never runs when the node is replaced, stranding whatever it had attached — the direct forwarding it replaced had no such gap. The cleanup is passed on now, and only when it really is a function, because React 18 warns about any other return value. The Escape fallback also called the consumer's `onEscapeKeyDown` before establishing that this dialog was the one the Escape belonged to. Every mounted dialog runs that listener, so a dialog underneath fired its handler for someone else's keystroke — and a `preventDefault()` there would have stopped the frontmost dialog from closing at all, since the lower listener registers first. The call now happens after the frontmost check. The gating has a regression test, verified to fail without the fix. The ref cleanup does not: React 18 ignores the returned function outright, so the repo's own test runtime cannot observe it. `packages/client` 488 tests, typecheck and static checks clean.
* fix: Load user keys without expiry metadata * fix: Avoid unused Azure user credential lookup
…t-AI#15753) * 📐 docs: Ask Pull Requests to Show the Mechanism The template asked for a brief summary and a test process, so descriptions land as a list of changed behavior with no view of how the change works. Reviewers then rebuild the call order from the diff. Summary now asks for the trigger and the resulting behavior. A new optional How it works section offers four views — a focused diff, a call tree, a shallow file tree, and a Mermaid sequence — with the instruction to pick one or two and delete the section when the summary already covers it. All guidance rides in HTML comments, so an unfilled template renders exactly as it does today. CLAUDE.md and AGENTS.md carry the same rule for agent-authored descriptions. Adapted from HumanLayer's show-me skill and an internal ClickHouse PR-template proposal. * 🧭 docs: Add Review and Completion Standards Both files describe how to write a change and how to test it, but not how a pull request gets from opened to done. That gap is where review rounds stall: a finding gets patched, the next review runs against an older head, and a working code path ships without its empty, failure or restored-session behavior. The new Review and Completion section states the review-cycle invariants — inline threads are the source of truth, findings are judged against the code, a review counts only for the commit it ran on, and repeated findings mean the subsystem needs a sweep — followed by a definition of done covering the observable experience, compatibility, and honest reporting of what was actually run. The reviewer and its trigger phrase are named as the part expected to change, so that subsection can be rewritten without touching the invariants around it. Nothing here restates the existing Testing, Typechecking or Frontend rules; it points at them. * 🧹 docs: Drop the Shipped Insights Design Note `docs/agent-insights-access-design.md` was the pre-implementation spec for LibreChat-AI#15549, and it says so: "design reference for work based on upstream/dev after PR LibreChat-AI#14898". Everything it specifies shipped — PermissionBits.VIEW_INSIGHTS = 16, /api/insights, the server-owned initial_agent_id — so the code and its tests are the source of truth now and the note can only drift from them. Nothing in the repository links to it. docs/skills-management-api.md stays: it describes a machine API surface that has no equivalent on librechat.ai, and its details still match packages/api/src/skills/management.ts.
…ndencies (LibreChat-AI#15754) * 🧶 docs: Keep the Breadcrumb Carve-Out in AGENTS.md The template and CLAUDE.md both say that naming the merged pull request which caused a bug is history the reader needs, not a breadcrumb. The condensed AGENTS.md version dropped that half, leaving a flat rule against referencing earlier work, which is the file most agents actually read. * 🧱 docs: Hold the Database Boundary and Make New Levers Configurable Workspace Boundaries said where database logic lives but not what may cross the line, so Mongoose types travel outward in exported signatures and make the storage engine part of each module's public API. That is the tax a second engine would pay, and it is cheaper to stop widening than to unwind: packages/api already imports mongoose in dozens of non-test files, while client carries none. Nothing in either file mentioned configSchema or librechat.yaml either, so a new limit or toggle lands as a constant by default and an operator cannot reach it. Both rules now sit in Workspace Boundaries, with the configurability half cross-referenced from the definition of done. Condensed into AGENTS.md as Module boundaries and configuration. * 🔌 docs: Take Dependencies, Do Not Reach for Them Client State Ownership already tells a frontend feature to accept app-global state through props rather than reaching into the store, and gives the reason: it is what lets the feature move to its own workspace later without a rewrite. The backend had no equivalent, and neither file mentioned dependency injection at all. Backend modules now carry the same rule one layer down, with createModels(mongoose) as the shape to copy and the static singletons under packages/api/src/mcp as the shape to stop extending. Integrations get it explicitly: an injected provider SDK, storage backend, vector store or OAuth server makes a second implementation a new argument instead of a new branch, and lets a test substitute at the boundary rather than mocking the module that holds it.
* fix: Settle Background Tool Polls Reliably * fix: recover interrupted background result delivery * fix: preserve independent background fallbacks * fix: align background claim types * chore: sort background task imports * fix: preserve claimed result narrowing
Both migration rules described an end state and left the trigger implicit, which is why they get skipped. "Keep /api changes to the absolute minimum" reads as "make a small edit here", the opposite of what it means, and it is phrased for new code while the common case is editing an existing CJS file. It now says /api holds wiring and not behavior, that minimum describes how much behavior /api gains rather than diff size, and that lifting the function out is the larger and correct diff. MCPRequestContext.js is named as the shape. "Convert the areas you touch" never defined an area, and never said an atom cannot be half converted, so mixed Recoil and Jotai imports in one file read as permission. The rule now states that new state is always Jotai, that the unit of conversion is one atom plus every reader and writer, and that an atom with a consumer outside the feature stays on Recoil and gets passed in. jotai-utils.ts is named for persisted atoms.
* fix: Skip test modules during structured tool discovery * fix: Keep tool discovery validation in TypeScript
* fix: expose tenant index upgrade migration * fix: retry tenant migration index builds * docs: run tenant migration from the image root
Covers the one leg the credential-free mock suite cannot fake: a file attached through the UI's Code Environment target must land in the live Code API, get delivered into the sandbox, and stay readable across turns. - fake-model: E2E_EXEC_UPLOADED:/E2E_EXEC_PERSIST: markers emit real bash_tool calls through the production tool pipeline (the sandbox surface stateful agents register; execute_code stays host-wired and never appears in the tool registry), plus an env-gated dump of each run's advertised tools for future spec authoring - spec: attach uploads a CSV to the Code API, turn 1 reads it back from /mnt/data and drops a proof file no upload contained, turn 2 (sent without an attachment) reads both — only possible when the run reuses the same stateful runtime session - e2e config: agents capabilities gain stateful_code_sessions, and the mock profile pins CHECK_BALANCE=false so a developer's local .env can never fail every send with zero-balance users - skips unless LIBRECHAT_CODE_BASEURL is provided (kept out of CI)
* fix: stop refreshing MCP caches on every chat message * fix: reconcile MCP caches independently of chat turns * fix: scope MCP polling to visible controls * fix: refresh status in the unpinned MCP submenu
* fix: Patch gRPC xDS DoS dependency * fix: Tidy patched gRPC module graph
…ibreChat-AI#15770) * chore: bump agents sdk to v3.8.5 * chore: bump nodemailer to v10.0.1 * chore: bump sharp to v0.35.4 * chore: bump js-yaml to v4.3.2 * chore: bump hono to v4.13.7 and @hono/node-server to v2.1.1
* feat: Select Attached Code Workspaces * fix: Preserve Workspace Boundaries Across Agent Graphs * fix: Validate Lazy Agent Workspace Bindings Before Persistence * fix: Include Handoff Environments in Workspace Selection * fix: Close Workspace Admission and Remote Ingress Gaps * fix: Preserve Workspace Selection In Chat Runtime Envelopes * fix: Authorize Deployment Workspace Status And Match Readiness * fix: Preserve Ephemeral Chats And Pin Runtime Worker Identity * test: Complete Deployment Pairing Fixtures
* ✨ feat: Cancel ordinary background tools * 🛡️ fix: Keep cancellation receipts rollout-safe * 🛡️ fix: Harden Background Tool Cancellation * 🧪 test: Cover cancellation config wiring * 🎨 style: Sort cancellation imports * 🧪 test: Align Cancellation Claim Result * 🛑 fix: Complete Background Cancellation Lifecycle * 🎨 style: Align Cancellation Test Formatting
…5777) * fix: Preserve BYOM foreground cancellation identity * fix: Bind foreground cancellation to runtime message
* 🕰️ feat: Add BYOM Bash Execution Timeouts * 🔧 fix: Type Bash Timeout Bounds * 🔒 fix: Bound BYOM Bash timeouts by policy * 🧪 test: Propagate BYOM timeout policy fixture
* fix: Preserve BYOM workspace selection state * 🧭 fix: Centralize Workspace Error Serialization * 🎨 style: Format Workspace Error Helper
…ibreChat-AI#15791) - send each turn through sendMessageAndWaitForCompletion so the composer is unlocked before the next Enter - give the three tests explicit timeouts, matching the chat.spec.ts budgets from LibreChat-AI#14740
* fix: resolve queued turn placeholder anchors * fix: anchor queued turns to durable message parents * fix: preserve durable queued turn anchors
) * fix: recover CodeAPI uploads after throttling * fix: cover eager CodeAPI uploads * fix: scope CodeAPI upload recovery * test: clarify upload recovery deadlines * fix: isolate CodeAPI recovery state * fix: cancel CodeAPI recovery waits * test: provide CodeAPI registry in handler fixture * fix: propagate CodeAPI recovery controls * fix: bound CodeAPI recovery by run * fix: preserve skill upload cancellation * test: update deferred loader signal contract * fix: preserve recovery cancellation * fix: cancel recovery source reads * fix: classify Axios upload cancellation * fix: preserve lazy provisioning cancellation * fix: close resource recovery boundaries * fix: preserve provisioning failures compatibly
* 🌊 fix: Prevent Agent Model Stream Idle Timeouts * fix: Preserve Default Proxy Dispatcher Construction * test: Assert Agent Transport Timeout Policy * test: Cover Direct Model Dispatcher Reuse * fix: Scope Agent Model Transport Timeouts * fix: Enforce Agent Timeouts Across Direct and Summary Clients * fix: Align Model Transport Adapter Types --------- Co-authored-by: Lia <lia@librechat.ai>
…16207) * docs: set expectations for AI-assisted contributions * docs: note that we may finish a contributor's branch --------- Co-authored-by: Lia <lia@librechat.ai>
* 🧮 fix: Show Live Tool Combo Multipliers * 🧮 fix: Preserve Live Outcome Separator * 🧮 fix: Narrow Combo Scan Parts * 🧮 test: Expect Live Combo Labels * 🎰 fix: Synchronize Live Combo Transitions * 🎰 test: Await Live Combo Reset * 🎰 perf: Accumulate Tool Combos During Span Summaries --------- Co-authored-by: Lia <lia@librechat.ai>
…-AI#16209) * fix: give each streamed tool call a stable outward index * fix: scope tool-call fragments and preserve completed answers * fix: finalize agent tool-call snapshots without duplicating deltas * fix: assemble complete tool identities before response publication --------- Co-authored-by: Lia <lia@librechat.ai>
…I#16214) After a repository moves to another owner, images publish under the new owner while existing deployments still pull the old namespace. The merge job now copies each published tag to LEGACY_GHCR_OWNER when that variable and the LEGACY_GHCR_TOKEN secret are set, using GHCR cross-repo blob mounts. It stays off while the legacy owner matches the publishing owner, and never fails the build.
* feat: add Claude Opus 5.5 support * fix: preserve Opus 5.5 settings through provider adapters * fix: preserve model-hidden settings when pruning parameters --------- Co-authored-by: Lia <lia@librechat.ai>
…at-AI#16220) The collapsed live activity row printed its repeated-tool multiplier at the row's far right, beside the chevron, a full row width away from the line it counts. It now sits directly after that line, the way the unfolded group already reads Create File x2, while the span's failure and cancellation verdict keeps the row's right edge because it belongs to the whole span. Co-authored-by: Lia <lia@librechat.ai>
…hat-AI#16223) * 🔍 fix: Enable File Search When Attaching From the Files Panel * 🔍 fix: Skip the Ephemeral File Search Flag for Saved Agents
…-AI#16224) * feat: support Cognito M2M for Agent management * fix: reject compound management scopes * fix: require scopes for audience-less management tokens
…I#16225) * Count the tool only while the line is its generic label A streamed intent already names the work this call is doing, so a repeat count beside it reads as a claim that the sentence happened N times. The count now rides the generic Running/Ran label alone, and is dropped for an intent, a failure or cancellation verdict, and a background handle line. * fix: Omit Repeat Counts From Sandbox Startup Labels * test: Bound Client Recovery Worker Activation Waits --------- Co-authored-by: Lia <lia@librechat.ai>
* feat: add GPT-6 Sol and Luna support * fix: normalize Sol and Luna reasoning requests * fix: align Sol and Luna effective Responses routing * fix: honor server routing policy for model uploads * test: await system grant indexes before writes * fix: verify model upload policies against runtime initialization --------- Co-authored-by: Lia <lia@librechat.ai>
…6228) * 🛍️ feat: Add Programming App to OpenRouter Categories * style: Format OpenRouter category override test --------- Co-authored-by: Lia <lia@librechat.ai>
…#16229) Co-authored-by: Lia <lia@librechat.ai>
…at-AI#16078) * Add client side tools * 🚦 fix: Ask the Model to Call Caller-Executed Tools Alone * Add client tool continuation replay tests * Fix clientTools docstring * fix: return only tools that have been actually applied * 🔁 fix: Continue a Caller-Executed Tool Exchange Across Requests * fix: handle null values for tool descriptions and parameters in client tools * 🧩 fix: Keep the Arguments of Every Tool Call in a Streamed Message * Fix PR comments * fix: Preserve client tool handoff across SDK event ordering --------- Co-authored-by: Lia <lia@librechat.ai>
* 🚀 v0.8.8-rc4 * chore: bump LibreChat chart to 2.0.14
…the Fetched Catalog New `models.filter` option for custom endpoints: serve `default` ∩ fetched instead of replacing `default` with everything the API returns, so several endpoints over one gateway can each offer their own slice of its catalog, in declared order. A failed or empty fetch falls back to the declared list, as it always has without `filter`. A filter-managed endpoint left with an empty model list renders as an empty picker entry and an unusable Agent Builder provider, so the endpoints route withholds it. User-provided endpoints are kept — their empty list reflects the user's own key — and the route resolves models only when some endpoint filters, failing open when it cannot. An empty filter-managed endpoint is unavailable, not being asked for an illegal model: stored conversations and agents naming it would otherwise earn their owners violations for a catalog change they had no part in. Both the chat and agents validation paths reject without logging one. One request resolves the models config from several places (model validation, token config, agent initialization), and each resolution may re-fetch gateway catalogs, so the resolved config is memoized per request in a WeakMap, evicting on failure so a later caller retries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add `modelLabels`, an endpoint-level record mapping model ids to display labels. Purely presentational: the id stays what is declared, fetched, selected, stored on the conversation and sent upstream, and a model with no entry renders its id. One helper, getModelName, resolves the agent name, the assistant name, or the declared label; the model list, search results, the selector's closed trigger, the selection announcement, the @-mention menu, the favourites list, the model parameter dropdowns, the endpoint settings panels, the added-conversation header and the Agent Builder read it or the declared map directly through getModelLabel. A declared label is additive in search via modelSearchNames — labelling a model never makes its id unsearchable — which also consolidates three inline copies of the name-resolution rule and fixes the globe icon for unnamed public agents in search results. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds an optional per-MCP-server `deferLoading: boolean` config flag. When true, every tool from that server defaults to deferred loading: the model receives the `tool_search` tool plus a name-only listing instead of each tool's full JSON schema, saving context on large tool sets. Unlike the per-agent `tool_options[toolId].defer_loading` toggle, it also applies to ephemeral agents (model + attached MCP) that carry no `tool_options`. Extends the existing `deferred_tools` capability (LibreChat-AI#11295) to the server-config level and the ephemeral path. Complementary to the proposed MCP toolFilter (LibreChat-AI#13346 / LibreChat-AI#11088).
…ntMessages crash (#64) The streaming content aggregator builds message content by index and yields a sparse array; an interrupted/partial save persists a hole that serializes to null in MongoDB. On replay, @librechat/agents formatAgentMessages reads part.type with no null guard and crashes. Sanitize content holes at getMessages, the single DB read chokepoint for conversation history, so both already-corrupted and future rows are neutralized for every consumer (formatAgentMessages, token counting, edit path). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…and avoid bans for unserved models
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rebuilds the fork stack on upstream
v0.8.8-rc4and supersedes #74 (sync/v0.8.8-rc2). Upstreammainwas 389 commits ahead ofsync/v0.8.8-rc2. This branch is 11 commits behind upstreammain: the post-rc4 fixes (LibreChat-AI#16231, LibreChat-AI#16252, LibreChat-AI#16261 agents SDK v3.9.3, LibreChat-AI#16266–LibreChat-AI#16270, LibreChat-AI#16253, LibreChat-AI#16005). That keeps it pinned to a tagged RC, like previous syncs did.It keeps the 2-layer commit stack from #74 (12 commits, same order, same authors).
sync/v0.8.8-rc2is left alone so #80 (which targets it) is not disturbed.Layer 1: upstream PR commits
0956e03e2aproorg/feat/custom-endpoint-model-filter, rebased on upstream on 2026-09-22) and squashed it back to one commit. The old cherry-pick conflicted with rc4 inagents/validation.tsandconfig-schemas.spec.ts.6d2f6ceccaproorg/feat/custom-endpoint-model-labels(2026-09-23)089771775deferLoadingaproorg/feat/mcp-server-defer-loading(2026-09-22)059ffd728Layer 2: fork-specific commits (unchanged in intent)
d3404df0enull content parts (#64) ·43e582733OIDC auto-refresh ·4379e8a57modelSpecs availability filter ·d6fa1c157empty model list / complement filter ·7851bd025declared custom endpoint over case-folded builtin ·17031ef93Locize i18n ·a14ea19bcMCP form-mode elicitation ·08dcdde48test mock alignmentConflict resolutions
packages/data-provider/src/types/assistants.ts: upstream movedPartMetadata/ContentPart/TMessageContentPartsintotypes/content.ts. TheAgents.ElicitationContent & ContentMetadataunion member now goes incontent.ts, and the old copy inassistants.tsis dropped.api/server/routes/mcp.js: kept the fork'scanAccessElicitationFlowand form validators. Dropped the re-addedclearGetTokensFlow, because upstream removed it and nothing calls it anymore.packages/api/src/mcp/MCPManager.ts: kept upstream's new constructor (catalog recovery tracker), theUpstreamTokenProviderResolverimport, and theonOAuthCredentialsChang(ed|ing)params. Added the elicitation slot tracking and theelicitationStreamId/elicitationStepIdparams alongside them.packages/api/src/flow/manager.ts,librechat.example.yaml: kept both sides.api/server/controllers/agents/client.js:stripUiOnlyContentPartsruns before upstream's (comment-only) summary-parts note.api/server/controllers/agents/responses.js: combined both wrappers intostripUnusableSummaryParts(stripUiOnlyContentParts(stripActivityLabelParts(allMessages))).SearchResults.tsx: the fork commit only reordered an import. Took upstream's version.ModelPanel.test.tsx: upstream now provides thePanelHeader/getModelLabelmocks itself. Took upstream's version.Fixes pulled forward from the upstream LibreChat-AI#15550 branch
stripUiOnlyContentPartsreturns the same array when nothing is stripped. Without this, rc4'sclient.test.js › derives and forwards compaction guidancefails itstoBe(payload)identity check. The form-elicitation commit also carried a stale copy ofruns.tsthat silently undid this change, so that hunk is removed from it.ElicitationForm.test.tsxmockslibrechat-data-providerwith...jest.requireActual. The rc4 provider icon registry readsProviderIdat import time, so a bare mock crashed the suite.Verification (run locally on this branch)
npm run build:data-provider && build:data-schemas && build:api && build:client-package: passclient:tsc --noEmit: 0 errorsnode --checkon all 640api/**/*.js: 0 failures. No conflict markers anywhere in the tree.apijest (mcp routes, agents controllers, Model/Endpoint controllers, validateModel, Config services, openIdJwtStrategy, AuthService): 1512 passed, 1 skippedpackages/data-providerjest: 2119 passedpackages/apijest (mcp, flow, agents, endpoints; CI ignore patterns): 7650 passed. The remaining failures are all caused by the sandbox, not by the sync:hardenedFetch.behavior.test.tsfails because loopback sockets are blocked. The fork doesn't touchsrc/mcp/oauth.endpoints/models.spec.tsandsummarization.e2e.test.tsfail because the host hadANTHROPIC_*/OPENAI_*set. With those unset (as in CI): 58 passed, 4 skipped.clientjest in every directory the stack touches: 287 suites, 4906 tests passed (after the ElicitationForm fix)Not run: the Redis/Mongo integration suites, Playwright e2e, and a deployed smoke test.
Note on the lockfile
Upstream's
package-lock.jsonat rc4 is unchanged by this stack. Installing needsNODE_ENV≠production(or--include=dev) to gettsdown.Supersedes #74.
🤖 Generated with Claude Code