Skip to content

Sync upstream v0.8.8-rc4 into apro-deploy - #81

Draft
busla wants to merge 1145 commits into
apro-deployfrom
sync/v0.8.8-rc4
Draft

busla wants to merge 1145 commits into
apro-deployfrom
sync/v0.8.8-rc4

Conversation

@busla

@busla busla commented Sep 24, 2026

Copy link
Copy Markdown

Summary

Rebuilds the fork stack on upstream v0.8.8-rc4 and supersedes #74 (sync/v0.8.8-rc2). Upstream main was 389 commits ahead of sync/v0.8.8-rc2. This branch is 11 commits behind upstream main: 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-rc2 is left alone so #80 (which targets it) is not disturbed.

Like #74, this is a rebased stack on the upstream tag, not a merge into apro-deploy. GitHub will show it as conflicting with apro-deploy. It is meant to replace apro-deploy, not merge into it.

Layer 1: upstream PR commits

Commit Upstream PR How it was rebuilt
0956e03e2 LibreChat-AI#15314 custom endpoint model filter Took the refreshed PR branch (aproorg/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 in agents/validation.ts and config-schemas.spec.ts.
6d2f6cecc LibreChat-AI#15311 model display labels Same approach, from aproorg/feat/custom-endpoint-model-labels (2026-09-23)
089771775 LibreChat-AI#13951 server-level deferLoading Same approach, from aproorg/feat/mcp-server-defer-loading (2026-09-22)
059ffd728 LibreChat-AI#15550 URL elicitation resume Cherry-picked, with conflicts resolved by hand (see below)

Layer 2: fork-specific commits (unchanged in intent)

d3404df0e null content parts (#64) · 43e582733 OIDC auto-refresh · 4379e8a57 modelSpecs availability filter · d6fa1c157 empty model list / complement filter · 7851bd025 declared custom endpoint over case-folded builtin · 17031ef93 Locize i18n · a14ea19bc MCP form-mode elicitation · 08dcdde48 test mock alignment

Conflict resolutions

  • packages/data-provider/src/types/assistants.ts: upstream moved PartMetadata / ContentPart / TMessageContentParts into types/content.ts. The Agents.ElicitationContent & ContentMetadata union member now goes in content.ts, and the old copy in assistants.ts is dropped.
  • api/server/routes/mcp.js: kept the fork's canAccessElicitationFlow and form validators. Dropped the re-added clearGetTokensFlow, because upstream removed it and nothing calls it anymore.
  • packages/api/src/mcp/MCPManager.ts: kept upstream's new constructor (catalog recovery tracker), the UpstreamTokenProviderResolver import, and the onOAuthCredentialsChang(ed|ing) params. Added the elicitation slot tracking and the elicitationStreamId/elicitationStepId params alongside them.
  • packages/api/src/flow/manager.ts, librechat.example.yaml: kept both sides.
  • api/server/controllers/agents/client.js: stripUiOnlyContentParts runs before upstream's (comment-only) summary-parts note.
  • api/server/controllers/agents/responses.js: combined both wrappers into stripUnusableSummaryParts(stripUiOnlyContentParts(stripActivityLabelParts(allMessages))).
  • SearchResults.tsx: the fork commit only reordered an import. Took upstream's version.
  • ModelPanel.test.tsx: upstream now provides the PanelHeader / getModelLabel mocks itself. Took upstream's version.

Fixes pulled forward from the upstream LibreChat-AI#15550 branch

  • stripUiOnlyContentParts returns the same array when nothing is stripped. Without this, rc4's client.test.js › derives and forwards compaction guidance fails its toBe(payload) identity check. The form-elicitation commit also carried a stale copy of runs.ts that silently undid this change, so that hunk is removed from it.
  • ElicitationForm.test.tsx mocks librechat-data-provider with ...jest.requireActual. The rc4 provider icon registry reads ProviderId at 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: pass
  • client: tsc --noEmit: 0 errors
  • node --check on all 640 api/**/*.js: 0 failures. No conflict markers anywhere in the tree.
  • api jest (mcp routes, agents controllers, Model/Endpoint controllers, validateModel, Config services, openIdJwtStrategy, AuthService): 1512 passed, 1 skipped
  • packages/data-provider jest: 2119 passed
  • packages/api jest (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.ts fails because loopback sockets are blocked. The fork doesn't touch src/mcp/oauth.
    • endpoints/models.spec.ts and summarization.e2e.test.ts fail because the host had ANTHROPIC_*/OPENAI_* set. With those unset (as in CI): 58 passed, 4 skipped.
  • client jest 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.json at rc4 is unchanged by this stack. Installing needs NODE_ENV ≠ production (or --include=dev) to get tsdown.

Supersedes #74.

🤖 Generated with Claude Code

ncchinh and others added 30 commits September 8, 2026 13:39
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
lia-by-librechat Bot and others added 28 commits September 22, 2026 08:30
* 🌊 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>
…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>
@coderabbitai

coderabbitai Bot commented Sep 24, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.